This is an automated email from the ASF dual-hosted git repository.

wuzhiguo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bigtop-manager.git


The following commit(s) were added to refs/heads/main by this push:
     new 68b888d1 BIGTOP-4392: Add unit test for command stage classes in 
server module (#192)
68b888d1 is described below

commit 68b888d1541686b7f58c1f243dccbef06519c1d4
Author: xianrenzw <139131974+xianre...@users.noreply.github.com>
AuthorDate: Tue Mar 25 17:10:11 2025 +0800

    BIGTOP-4392: Add unit test for command stage classes in server module (#192)
---
 .../command/stage/AbstractComponentStage.java      |   2 +-
 .../server/command/stage/AbstractStage.java        |   5 -
 .../command/stage/AbstractComponentStageTest.java  |  91 ++++
 .../command/stage/ComponentAddStageTest.java       |  76 +++
 .../command/stage/ComponentCheckStageTest.java     |  76 +++
 .../command/stage/ComponentConfigureStageTest.java |  76 +++
 .../command/stage/ComponentInitStageTest.java      |  76 +++
 .../command/stage/ComponentPrepareStageTest.java   |  76 +++
 .../server/command/stage/ComponentStageTest.java   | 159 ------
 .../command/stage/ComponentStartStageTest.java     |  76 +++
 .../command/stage/ComponentStopStageTest.java      |  76 +++
 .../server/command/stage/HostCheckStageTest.java   |  82 +---
 .../server/command/stage/SetupJdkStageTest.java    |  82 +---
 .../server/command/stage/StageContextTest.java     | 189 ++++++++
 .../services/kafka/configuration/kafka-broker.xml  | 537 +++++++++++++++++++++
 .../services/kafka/configuration/kafka-env.xml     |  71 +++
 .../services/kafka/configuration/kafka-log4j.xml   | 143 ++++++
 .../services/kafka/configuration/kafka.conf.xml    |  54 +++
 .../bigtop/3.3.0/services/kafka/metainfo.xml       |  63 +++
 .../stacks/bigtop/3.3.0/services/kafka/order.json  |   8 +
 .../services/zookeeper/configuration/zoo.cfg.xml   | 128 +++++
 .../zookeeper/configuration/zookeeper-env.xml      |  58 +++
 .../bigtop/3.3.0/services/zookeeper/metainfo.xml   |  64 +++
 .../bigtop/3.3.0/services/zookeeper/order.json     |   5 +
 24 files changed, 1980 insertions(+), 293 deletions(-)

diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStage.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStage.java
index 8d35b8d8..ce610b8b 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStage.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStage.java
@@ -85,7 +85,7 @@ public abstract class AbstractComponentStage extends 
AbstractStage {
         return taskContext;
     }
 
-    private Map<String, List<String>> getClusterHosts() {
+    protected Map<String, List<String>> getClusterHosts() {
         Map<String, List<String>> clusterHosts = new HashMap<>();
         for (ClusterPO clusterPO : clusterDao.findAll()) {
             List<String> hosts = new ArrayList<>();
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractStage.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractStage.java
index 15b821cd..47f99871 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractStage.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/command/stage/AbstractStage.java
@@ -161,9 +161,4 @@ public abstract class AbstractStage implements Stage {
 
         return stagePO;
     }
-
-    protected void setStageContextAndTasksForTest(StageContext stageContext, 
List<Task> tasks) {
-        this.stageContext = stageContext;
-        this.tasks = tasks;
-    }
 }
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStageTest.java
new file mode 100644
index 00000000..c921a054
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/AbstractComponentStageTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.dao.po.ClusterPO;
+import org.apache.bigtop.manager.server.command.task.TaskContext;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class AbstractComponentStageTest {
+
+    @Mock
+    private AbstractComponentStage stage;
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testCreateTaskContext() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ClusterPO clusterPO = new ClusterPO();
+        clusterPO.setId(1L);
+        clusterPO.setName("test");
+        clusterPO.setRootDir("/opt");
+        clusterPO.setUserGroup("test");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+        ReflectionTestUtils.setField(stage, "clusterPO", clusterPO);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        Map<String, List<String>> componentHosts = new HashMap<>();
+        componentHosts.put("test", List.of("host1"));
+
+        when(stage.getClusterHosts()).thenReturn(componentHosts);
+        doCallRealMethod().when(stage).createTaskContext(any());
+        TaskContext taskContext = stage.createTaskContext("host1");
+
+        assertEquals(1L, taskContext.getClusterId());
+        assertEquals("test", taskContext.getClusterName());
+        assertEquals("zookeeper", taskContext.getServiceName());
+        assertEquals("zookeeper_server", taskContext.getComponentName());
+        assertEquals("ZooKeeper Server", 
taskContext.getComponentDisplayName());
+        assertEquals("zookeeper", taskContext.getServiceUser());
+        assertEquals("test", taskContext.getUserGroup());
+        assertEquals("/opt", taskContext.getRootDir());
+
+        Map<String, Object> properties = taskContext.getProperties();
+        Map<String, List<String>> clusterHosts = (Map<String, List<String>>) 
properties.get("clusterHosts");
+        assertEquals(1, taskContext.getProperties().size());
+        assertEquals(1, clusterHosts.size());
+        assertEquals("host1", clusterHosts.get("test").get(0));
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentAddStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentAddStageTest.java
new file mode 100644
index 00000000..e2b213e5
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentAddStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentAddTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentAddStageTest {
+
+    @Mock
+    private ComponentAddStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentAddTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentAddTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Add ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentCheckStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentCheckStageTest.java
new file mode 100644
index 00000000..2db9eda9
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentCheckStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentCheckTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentCheckStageTest {
+
+    @Mock
+    private ComponentCheckStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentCheckTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentCheckTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Check ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentConfigureStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentConfigureStageTest.java
new file mode 100644
index 00000000..0967eb0a
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentConfigureStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentConfigureTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentConfigureStageTest {
+
+    @Mock
+    private ComponentConfigureStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentConfigureTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentConfigureTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Configure ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentInitStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentInitStageTest.java
new file mode 100644
index 00000000..9457b703
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentInitStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentInitTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentInitStageTest {
+
+    @Mock
+    private ComponentInitStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentInitTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentInitTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Init ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentPrepareStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentPrepareStageTest.java
new file mode 100644
index 00000000..20339b66
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentPrepareStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentPrepareTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentPrepareStageTest {
+
+    @Mock
+    private ComponentPrepareStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentPrepareTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentPrepareTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Prepare ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStageTest.java
deleted file mode 100644
index c8a19bed..00000000
--- 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStageTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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
- *
- *    https://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.bigtop.manager.server.command.stage;
-
-import org.apache.bigtop.manager.dao.po.StagePO;
-import org.apache.bigtop.manager.dao.repository.ClusterDao;
-import org.apache.bigtop.manager.dao.repository.HostDao;
-import org.apache.bigtop.manager.dao.repository.StageDao;
-import org.apache.bigtop.manager.server.command.task.Task;
-import org.apache.bigtop.manager.server.holder.SpringContextHolder;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.MockedStatic;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-public class ComponentStageTest {
-
-    private MockedStatic<SpringContextHolder> springContextHolderMockedStatic;
-
-    @Mock
-    private StageDao stageDao;
-
-    @Mock
-    private HostDao hostDao;
-
-    @Mock
-    private ClusterDao clusterDao;
-
-    @Spy
-    private StageContext stageContext;
-
-    @Mock
-    private List<Task> tasks;
-
-    @Spy
-    private StagePO stagePO;
-
-    private ComponentAddStage componentStage;
-
-    @BeforeEach
-    public void setUp() {
-        springContextHolderMockedStatic = 
mockStatic(SpringContextHolder.class);
-        when(SpringContextHolder.getBean(StageDao.class)).thenReturn(stageDao);
-        when(SpringContextHolder.getBean(HostDao.class)).thenReturn(hostDao);
-        
when(SpringContextHolder.getBean(ClusterDao.class)).thenReturn(clusterDao);
-
-        componentStage = mock(ComponentAddStage.class);
-
-        stageContext.setComponentName("TestComponentName");
-        stageContext.setServiceName("TestServiceName");
-        stageContext.setClusterId(123L);
-
-        
doCallRealMethod().when(componentStage).setStageContextAndTasksForTest(any(), 
any());
-        componentStage.setStageContextAndTasksForTest(stageContext, tasks);
-
-        doCallRealMethod().when(componentStage).injectBeans();
-        componentStage.injectBeans();
-
-        doCallRealMethod().when(componentStage).loadStagePO(any());
-        lenient().when(componentStage.getStagePO()).thenCallRealMethod();
-        componentStage.loadStagePO(stagePO);
-    }
-
-    @AfterEach
-    public void tearDown() {
-        springContextHolderMockedStatic.close();
-    }
-
-    @Test
-    public void testInjectBeans() {
-        springContextHolderMockedStatic.verify(() -> 
SpringContextHolder.getBean(any(Class.class)), times(3));
-    }
-
-    @Test
-    public void testBeforeCreateTasks() {
-        doCallRealMethod().when(componentStage).beforeCreateTasks();
-        componentStage.beforeCreateTasks();
-        verify(clusterDao, times(1)).findById(123L);
-    }
-
-    @Test
-    public void tesGetServiceName() {
-        doCallRealMethod().when(componentStage).getServiceName();
-        assertEquals("TestServiceName", componentStage.getServiceName());
-    }
-
-    @Test
-    public void tesGetComponentName() {
-        doCallRealMethod().when(componentStage).getComponentName();
-        assertEquals("TestComponentName", componentStage.getComponentName());
-    }
-
-    @Test
-    public void testBeforeRun() {
-        doCallRealMethod().when(componentStage).beforeRun();
-        componentStage.beforeRun();
-        verify(stageDao, times(1)).partialUpdateById(any());
-    }
-
-    @Test
-    public void testOnSuccess() {
-        doCallRealMethod().when(componentStage).onSuccess();
-        componentStage.onSuccess();
-        verify(stageDao, times(1)).partialUpdateById(any());
-    }
-
-    @Test
-    public void testOnFailure() {
-        doCallRealMethod().when(componentStage).onFailure();
-        componentStage.onFailure();
-        verify(stageDao, times(1)).partialUpdateById(any());
-    }
-
-    @Test
-    public void testGetStageContext() {
-        doCallRealMethod().when(componentStage).getStageContext();
-        assertEquals(stageContext, componentStage.getStageContext());
-    }
-
-    @Test
-    public void testGetTasks() {
-        doCallRealMethod().when(componentStage).getTasks();
-        assertEquals(tasks, componentStage.getTasks());
-    }
-}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStartStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStartStageTest.java
new file mode 100644
index 00000000..d625e8e8
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStartStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentStartTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentStartStageTest {
+
+    @Mock
+    private ComponentStartStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentStartTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentStartTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Start ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStopStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStopStageTest.java
new file mode 100644
index 00000000..de6e0b54
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/ComponentStopStageTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.common.utils.Environments;
+import org.apache.bigtop.manager.server.command.task.ComponentStopTask;
+import org.apache.bigtop.manager.server.command.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentStopStageTest {
+
+    @Mock
+    private ComponentStopStage stage;
+
+    @Test
+    public void testCreateTask() {
+        MockedConstruction<?> mocked = 
mockConstruction(ComponentStopTask.class);
+
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
+
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(ComponentStopTask.class, task);
+
+        mocked.close();
+    }
+
+    @Test
+    public void testGetName() {
+        StageContext stageContext = new StageContext();
+        stageContext.setServiceName("zookeeper");
+        stageContext.setComponentName("zookeeper_server");
+
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
+
+        MockedStatic<Environments> mocked = mockStatic(Environments.class);
+        when(Environments.isDevMode()).thenReturn(true);
+
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Stop ZooKeeper Server", stage.getName());
+
+        mocked.close();
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/HostCheckStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/HostCheckStageTest.java
index 317ccd82..285d47ed 100644
--- 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/HostCheckStageTest.java
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/HostCheckStageTest.java
@@ -18,93 +18,47 @@
  */
 package org.apache.bigtop.manager.server.command.stage;
 
-import org.apache.bigtop.manager.dao.po.StagePO;
-import org.apache.bigtop.manager.dao.repository.HostDao;
-import org.apache.bigtop.manager.dao.repository.StageDao;
+import org.apache.bigtop.manager.server.command.task.HostCheckTask;
 import org.apache.bigtop.manager.server.command.task.Task;
-import org.apache.bigtop.manager.server.holder.SpringContextHolder;
 
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
-import org.mockito.MockedStatic;
-import org.mockito.Spy;
+import org.mockito.MockedConstruction;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
 
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mockConstruction;
 
 @ExtendWith(MockitoExtension.class)
 public class HostCheckStageTest {
 
-    private MockedStatic<SpringContextHolder> springContextHolderMockedStatic;
-
     @Mock
-    private StageDao stageDao;
-
-    @Mock
-    private HostDao hostDao;
-
-    @Spy
-    private StageContext stageContext;
-
-    @Mock
-    private List<Task> tasks;
-
-    @Spy
-    private StagePO stagePO;
-
-    private HostCheckStage hostCheckStage;
+    private HostCheckStage stage;
 
-    @BeforeEach
-    public void setUp() {
-        springContextHolderMockedStatic = 
mockStatic(SpringContextHolder.class);
-        when(SpringContextHolder.getBean(StageDao.class)).thenReturn(stageDao);
-        when(SpringContextHolder.getBean(HostDao.class)).thenReturn(hostDao);
-
-        hostCheckStage = mock(HostCheckStage.class);
+    @Test
+    public void testCreateTask() {
+        StageContext stageContext = new StageContext();
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
 
-        
doCallRealMethod().when(hostCheckStage).setStageContextAndTasksForTest(any(), 
any());
-        hostCheckStage.setStageContextAndTasksForTest(stageContext, tasks);
+        MockedConstruction<?> mocked = mockConstruction(HostCheckTask.class);
 
-        doCallRealMethod().when(hostCheckStage).injectBeans();
-        hostCheckStage.injectBeans();
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
 
-        doCallRealMethod().when(hostCheckStage).loadStagePO(any());
-        lenient().when(hostCheckStage.getStagePO()).thenCallRealMethod();
-        hostCheckStage.loadStagePO(stagePO);
-    }
-
-    @AfterEach
-    public void tearDown() {
-        springContextHolderMockedStatic.close();
-    }
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(HostCheckTask.class, task);
 
-    @Test
-    public void testInjectBeans() {
-        springContextHolderMockedStatic.verify(() -> 
SpringContextHolder.getBean(any(Class.class)), times(2));
-    }
-
-    @Test
-    public void testBeforeCreateTasks() {
-        doCallRealMethod().when(hostCheckStage).beforeCreateTasks();
-        assertDoesNotThrow(() -> hostCheckStage.beforeCreateTasks());
+        mocked.close();
     }
 
     @Test
     public void tesGetName() {
-        doCallRealMethod().when(hostCheckStage).getName();
-        assertEquals("Check hosts", hostCheckStage.getName());
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Check hosts", stage.getName());
     }
 }
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/SetupJdkStageTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/SetupJdkStageTest.java
index 0c18be92..3f11cf8f 100644
--- 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/SetupJdkStageTest.java
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/SetupJdkStageTest.java
@@ -18,93 +18,47 @@
  */
 package org.apache.bigtop.manager.server.command.stage;
 
-import org.apache.bigtop.manager.dao.po.StagePO;
-import org.apache.bigtop.manager.dao.repository.HostDao;
-import org.apache.bigtop.manager.dao.repository.StageDao;
+import org.apache.bigtop.manager.server.command.task.SetupJdkTask;
 import org.apache.bigtop.manager.server.command.task.Task;
-import org.apache.bigtop.manager.server.holder.SpringContextHolder;
 
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
-import org.mockito.MockedStatic;
-import org.mockito.Spy;
+import org.mockito.MockedConstruction;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
 
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mockConstruction;
 
 @ExtendWith(MockitoExtension.class)
 public class SetupJdkStageTest {
 
-    private MockedStatic<SpringContextHolder> springContextHolderMockedStatic;
-
     @Mock
-    private StageDao stageDao;
-
-    @Mock
-    private HostDao hostDao;
-
-    @Spy
-    private StageContext stageContext;
-
-    @Mock
-    private List<Task> tasks;
-
-    @Spy
-    private StagePO stagePO;
-
-    private SetupJdkStage setupJdkStage;
+    private SetupJdkStage stage;
 
-    @BeforeEach
-    public void setUp() {
-        springContextHolderMockedStatic = 
mockStatic(SpringContextHolder.class);
-        when(SpringContextHolder.getBean(StageDao.class)).thenReturn(stageDao);
-        when(SpringContextHolder.getBean(HostDao.class)).thenReturn(hostDao);
-
-        setupJdkStage = mock(SetupJdkStage.class);
+    @Test
+    public void testCreateTask() {
+        StageContext stageContext = new StageContext();
+        ReflectionTestUtils.setField(stage, "stageContext", stageContext);
 
-        
doCallRealMethod().when(setupJdkStage).setStageContextAndTasksForTest(any(), 
any());
-        setupJdkStage.setStageContextAndTasksForTest(stageContext, tasks);
+        MockedConstruction<?> mocked = mockConstruction(SetupJdkTask.class);
 
-        doCallRealMethod().when(setupJdkStage).injectBeans();
-        setupJdkStage.injectBeans();
+        doCallRealMethod().when(stage).createTask(any());
+        Task task = stage.createTask("host1");
 
-        doCallRealMethod().when(setupJdkStage).loadStagePO(any());
-        lenient().when(setupJdkStage.getStagePO()).thenCallRealMethod();
-        setupJdkStage.loadStagePO(stagePO);
-    }
-
-    @AfterEach
-    public void tearDown() {
-        springContextHolderMockedStatic.close();
-    }
+        assertEquals(1, mocked.constructed().size());
+        assertInstanceOf(SetupJdkTask.class, task);
 
-    @Test
-    public void testInjectBeans() {
-        springContextHolderMockedStatic.verify(() -> 
SpringContextHolder.getBean(any(Class.class)), times(2));
-    }
-
-    @Test
-    public void testBeforeCreateTasks() {
-        doCallRealMethod().when(setupJdkStage).beforeCreateTasks();
-        assertDoesNotThrow(() -> setupJdkStage.beforeCreateTasks());
+        mocked.close();
     }
 
     @Test
     public void tesGetName() {
-        doCallRealMethod().when(setupJdkStage).getName();
-        assertEquals("Setup JDK", setupJdkStage.getName());
+        doCallRealMethod().when(stage).getName();
+        assertEquals("Setup JDK", stage.getName());
     }
 }
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/StageContextTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/StageContextTest.java
new file mode 100644
index 00000000..5745f138
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/command/stage/StageContextTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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
+ *
+ *    https://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.bigtop.manager.server.command.stage;
+
+import org.apache.bigtop.manager.dao.po.ClusterPO;
+import org.apache.bigtop.manager.dao.repository.ClusterDao;
+import org.apache.bigtop.manager.server.enums.CommandLevel;
+import org.apache.bigtop.manager.server.holder.SpringContextHolder;
+import org.apache.bigtop.manager.server.model.dto.CommandDTO;
+import org.apache.bigtop.manager.server.model.dto.HostDTO;
+import org.apache.bigtop.manager.server.model.dto.command.ClusterCommandDTO;
+import org.apache.bigtop.manager.server.model.dto.command.HostCommandDTO;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class StageContextTest {
+
+    @Mock
+    private ClusterDao clusterDao;
+
+    private MockedStatic<SpringContextHolder> springContextHolderMockedStatic;
+
+    @BeforeEach
+    void setUp() {
+        springContextHolderMockedStatic = 
mockStatic(SpringContextHolder.class);
+        
when(SpringContextHolder.getBean(ClusterDao.class)).thenReturn(clusterDao);
+    }
+
+    @AfterEach
+    void tearDown() {
+        springContextHolderMockedStatic.close();
+    }
+
+    @Test
+    void testWithClusterIdAndClusterCommand() {
+        // Mock database cluster info
+        ClusterPO mockCluster = new ClusterPO();
+        mockCluster.setId(1L);
+        mockCluster.setName("test-cluster");
+        mockCluster.setUserGroup("test-user-group");
+        mockCluster.setRootDir("/data/bigtop");
+        when(clusterDao.findById(1L)).thenReturn(mockCluster);
+
+        // Create test HostDTOs
+        HostDTO host1 = new HostDTO();
+        host1.setHostnames(List.of("host1.example.com"));
+        HostDTO host2 = new HostDTO();
+        host2.setHostnames(List.of("host2.example.com"));
+
+        // Build CLUSTER-level command
+        ClusterCommandDTO clusterCommand = new ClusterCommandDTO();
+        clusterCommand.setHosts(List.of(host1, host2));
+        clusterCommand.setName("should-not-use-this-name");
+        clusterCommand.setUserGroup("should-not-use-this-group");
+        clusterCommand.setRootDir("/should/not/use/this");
+
+        CommandDTO commandDTO = new CommandDTO();
+        commandDTO.setClusterId(1L);
+        commandDTO.setCommandLevel(CommandLevel.CLUSTER);
+        commandDTO.setClusterCommand(clusterCommand);
+
+        // Execute conversion
+        StageContext context = StageContext.fromCommandDTO(commandDTO);
+
+        // Verify cluster info comes from database
+        assertEquals(1L, context.getClusterId());
+        assertEquals("test-cluster", context.getClusterName());
+        assertEquals("test-user-group", context.getUserGroup());
+        assertEquals("/data/bigtop", context.getRootDir());
+
+        // Verify hostnames not set (skip processing when clusterId exists)
+        assertNull(context.getHostnames());
+
+        // Ensure redundant command info is not used
+        assertEquals("test-cluster", context.getClusterName());
+    }
+
+    @Test
+    void testClusterCommandWithoutClusterId() {
+        // Create test HostDTOs
+        HostDTO host1 = new HostDTO();
+        host1.setHostnames(List.of("hostA.example.com"));
+        HostDTO host2 = new HostDTO();
+        host2.setHostnames(List.of("hostB.example.com"));
+
+        // Build CLUSTER-level command (no clusterId)
+        ClusterCommandDTO clusterCommand = new ClusterCommandDTO();
+        clusterCommand.setHosts(List.of(host1, host2));
+        clusterCommand.setName("new-cluster");
+        clusterCommand.setUserGroup("new-group");
+        clusterCommand.setRootDir("/new/root");
+
+        CommandDTO commandDTO = new CommandDTO();
+        commandDTO.setClusterId(null);
+        commandDTO.setCommandLevel(CommandLevel.CLUSTER);
+        commandDTO.setClusterCommand(clusterCommand);
+
+        // Execute conversion
+        StageContext context = StageContext.fromCommandDTO(commandDTO);
+
+        // Verify info comes from command payload
+        assertNull(context.getClusterId());
+        assertEquals(List.of("hostA.example.com", "hostB.example.com"), 
context.getHostnames());
+        assertEquals("new-cluster", context.getClusterName());
+        assertEquals("new-group", context.getUserGroup());
+        assertEquals("/new/root", context.getRootDir());
+    }
+
+    @Test
+    void testHostCommand() {
+        // Create HostCommandDTO test objects
+        HostCommandDTO host1 = new HostCommandDTO();
+        host1.setHostnames(List.of("host-01.example.com", 
"host-01-alias.example.com"));
+        HostCommandDTO host2 = new HostCommandDTO();
+        host2.setHostnames(List.of("host-02.example.com"));
+
+        CommandDTO commandDTO = new CommandDTO();
+        commandDTO.setCommandLevel(CommandLevel.HOST);
+        commandDTO.setHostCommands(List.of(host1, host2));
+
+        // Execute conversion
+        StageContext context = StageContext.fromCommandDTO(commandDTO);
+
+        // Verify merged hostnames
+        assertEquals(
+                List.of("host-01.example.com", "host-01-alias.example.com", 
"host-02.example.com"),
+                context.getHostnames());
+
+        // Verify other fields not set
+        assertNull(context.getClusterId());
+        assertNull(context.getClusterName());
+    }
+
+    @Test
+    void testServiceCommand() {
+        // Build SERVICE-level command
+        CommandDTO commandDTO = new CommandDTO();
+        commandDTO.setCommandLevel(CommandLevel.SERVICE);
+
+        StageContext context = StageContext.fromCommandDTO(commandDTO);
+
+        // Verify no other fields set
+        assertNull(context.getClusterId());
+        assertNull(context.getHostnames());
+    }
+
+    @Test
+    void testComponentCommand() {
+        // Build component level command
+        CommandDTO commandDTO = new CommandDTO();
+        commandDTO.setCommandLevel(CommandLevel.COMPONENT);
+
+        StageContext context = StageContext.fromCommandDTO(commandDTO);
+
+        // Verify no other fields set
+        assertNull(context.getClusterId());
+        assertNull(context.getHostnames());
+    }
+}
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-broker.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-broker.xml
new file mode 100644
index 00000000..2e915dc9
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-broker.xml
@@ -0,0 +1,537 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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
+  ~
+  ~    https://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.
+-->
+
+<configuration>
+    <property>
+        <name>log.dirs</name>
+        <display-name>Log directories</display-name>
+        <value>/kafka-logs</value>
+        <description>
+            A comma-separated list of one or more directories in which Kafka 
data is stored.
+            Each new partition that is created will be placed in the directory 
which currently has the fewest
+            partitions.
+        </description>
+    </property>
+    <property>
+        <name>zookeeper.connect</name>
+        <value><![CDATA[<#if zk_server_list?? ><#list zk_server_list as 
host>${host}:2181<#sep>,</#sep></#list><#else>localhost:2181</#if>]]></value>
+        <description>
+            Zookeeper also allows you to add a "chroot" path which will make 
all kafka data for this cluster appear
+            under a particular path.
+            This is a way to setup multiple Kafka clusters or other 
applications on the same zookeeper cluster.
+            To do this give a connection string in the form 
hostname1:port1,hostname2:port2,hostname3:port3/chroot/path
+            which would put all this cluster's data under the path 
/chroot/path. Note that consumers must use the same
+            connection string.
+        </description>
+    </property>
+    <property>
+        <name>listeners</name>
+        <value><![CDATA[PLAINTEXT://<#if host?? 
>${host}:9092<#else>localhost:9092</#if>]]></value>
+        <description>
+            host and port where kafka broker will be accepting connections. 
localhost will be substituted with hostname.
+        </description>
+    </property>
+    <property>
+        <name>advertised.listeners</name>
+        <value><![CDATA[PLAINTEXT://<#if host?? 
>${host}:9092<#else>localhost:9092</#if>]]></value>
+        <description>
+            Listeners to publish to ZooKeeper for clients to use, if different 
than the listeners config property.
+        </description>
+    </property>
+    <property>
+        <name>message.max.bytes</name>
+        <value>1000000</value>
+        <description>
+            The maximum size of a message that the server can receive.
+            It is important that this property be in sync with the maximum 
fetch size your consumers use or
+            else an unruly producer will be able to publish messages too large 
for consumers to consume.
+        </description>
+    </property>
+    <property>
+        <name>num.network.threads</name>
+        <value>3</value>
+        <description>
+            The number of network threads that the server uses for handling 
network requests.
+            You probably don't need to change this.
+        </description>
+    </property>
+    <property>
+        <name>num.io.threads</name>
+        <value>8</value>
+        <description>
+            The number of I/O threads that the server uses for executing 
requests. You should have at least as many
+            threads as you have disks.
+        </description>
+    </property>
+    <property>
+        <name>queued.max.requests</name>
+        <value>500</value>
+        <description>
+            The number of requests that can be queued up for processing by the 
I/O threads before the network threads
+            stop reading in new requests.
+        </description>
+    </property>
+    <property>
+        <name>socket.send.buffer.bytes</name>
+        <value>102400</value>
+        <description>The SO_SNDBUFF buffer the server prefers for socket 
connections.</description>
+    </property>
+    <property>
+        <name>socket.receive.buffer.bytes</name>
+        <value>102400</value>
+        <description>The SO_RCVBUFF buffer the server prefers for socket 
connections.</description>
+    </property>
+    <property>
+        <name>socket.request.max.bytes</name>
+        <value>104857600</value>
+        <description>
+            The maximum request size the server will allow. This prevents the 
server from running out of memory and
+            should be smaller than the Java heap size.
+        </description>
+    </property>
+    <property>
+        <name>num.partitions</name>
+        <value>1</value>
+        <description>The default number of partitions per topic.</description>
+    </property>
+    <property>
+        <name>log.segment.bytes</name>
+        <value>1073741824</value>
+        <description>
+            The maximum request size the server will allow.
+            This prevents the server from running out of memory and should be 
smaller than the Java heap size.
+        </description>
+    </property>
+    <property>
+        <name>log.roll.hours</name>
+        <value>168</value>
+        <description>
+            This setting will force Kafka to roll a new log segment even if 
the log.segment.bytes size has not been
+            reached.
+        </description>
+    </property>
+    <property>
+        <name>log.retention.bytes</name>
+        <value>-1</value>
+        <description>
+            The amount of data to retain in the log for each topic-partitions. 
Note that this is the limit per-partition
+            so multiply by the number of partitions to get the total data 
retained for the topic.
+            Also note that if both log.retention.hours and log.retention.bytes 
are both set we delete a segment when
+            either limit is exceeded.
+        </description>
+    </property>
+    <property>
+        <name>log.retention.hours</name>
+        <value>168</value>
+        <description>
+            The number of hours to keep a log segment before it is deleted, 
i.e. the default data retention window for
+            all topics.
+            Note that if both log.retention.hours and log.retention.bytes are 
both set we delete a segment when either
+            limit is exceeded.
+        </description>
+    </property>
+    <property>
+        <name>log.cleanup.interval.mins</name>
+        <value>10</value>
+        <description>
+            The frequency in minutes that the log cleaner checks whether any 
log segment is eligible for deletion to
+            meet the retention policies.
+        </description>
+    </property>
+    <property>
+        <name>log.retention.check.interval.ms</name>
+        <value>600000</value>
+        <description>
+            The frequency in milliseconds that the log cleaner checks whether 
any log segment is eligible for deletion
+            to meet the retention policies.
+        </description>
+    </property>
+    <property>
+        <name>log.index.size.max.bytes</name>
+        <value>10485760</value>
+        <description>
+            The maximum size in bytes we allow for the offset index for each 
log segment.
+            Note that we will always pre-allocate a sparse file with this much 
space and shrink it down when the log
+            rolls.
+            If the index fills up we will roll a new log segment even if we 
haven't reached the log.segment.bytes limit.
+        </description>
+    </property>
+    <property>
+        <name>log.index.interval.bytes</name>
+        <value>4096</value>
+        <description>
+            The byte interval at which we add an entry to the offset index.
+            When executing a fetch request the server must do a linear scan 
for up to this many bytes to find the
+            correct position in the log to begin and end the fetch.
+            So setting this value to be larger will mean larger index files 
(and a bit more memory usage) but less
+            scanning.
+            However the server will never add more than one index entry per 
log append (even if more than
+            log.index.interval worth of messages are appended).
+            In general you probably don't need to mess with this value.
+        </description>
+    </property>
+    <property>
+        <name>auto.create.topics.enable</name>
+        <value>true</value>
+        <description>
+            Enable auto creation of topic on the server.
+            If this is set to true then attempts to produce, consume, or fetch 
metadata for a non-existent topic will
+            automatically create it with the default replication factor and 
number of partitions.
+        </description>
+    </property>
+    <property>
+        <name>controller.socket.timeout.ms</name>
+        <value>30000</value>
+        <description>
+            The socket timeout for commands from the partition management 
controller to the replicas.
+        </description>
+    </property>
+    <property>
+        <name>controller.message.queue.size</name>
+        <value>10</value>
+        <description>The buffer size for 
controller-to-broker-channels</description>
+    </property>
+    <property>
+        <name>default.replication.factor</name>
+        <value>1</value>
+        <description>The default replication factor for automatically created 
topics.</description>
+    </property>
+    <property>
+        <name>replica.lag.time.max.ms</name>
+        <value>10000</value>
+        <description>
+            If a follower hasn't sent any fetch requests for this window of 
time, the leader will remove the follower
+            from ISR (in-sync replicas) and treat it as dead.
+        </description>
+    </property>
+    <property>
+        <name>replica.lag.max.messages</name>
+        <value>4000</value>
+        <description>
+            If a replica falls more than this many messages behind the leader, 
the leader will remove the follower from
+            ISR and treat it as dead.
+        </description>
+    </property>
+    <property>
+        <name>replica.socket.timeout.ms</name>
+        <value>30000</value>
+        <description>The socket timeout for network requests to the leader for 
replicating data.</description>
+    </property>
+    <property>
+        <name>replica.socket.receive.buffer.bytes</name>
+        <value>65536</value>
+        <description>The socket receive buffer for network requests to the 
leader for replicating data.</description>
+    </property>
+    <property>
+        <name>replica.fetch.max.bytes</name>
+        <value>1048576</value>
+        <description>
+            The number of byes of messages to attempt to fetch for each 
partition in the fetch requests the replicas
+            send to the leader.
+        </description>
+    </property>
+    <property>
+        <name>replica.fetch.wait.max.ms</name>
+        <value>500</value>
+        <description>
+            The maximum amount of time to wait time for data to arrive on the 
leader in the fetch requests sent by the
+            replicas to the leader.
+        </description>
+    </property>
+    <property>
+        <name>replica.fetch.min.bytes</name>
+        <value>1</value>
+        <description>
+            Minimum bytes expected for each fetch response for the fetch 
requests from the replica to the leader.
+            If not enough bytes, wait up to replica.fetch.wait.max.ms for this 
many bytes to arrive.
+        </description>
+    </property>
+    <property>
+        <name>num.replica.fetchers</name>
+        <value>1</value>
+        <description>
+            Number of threads used to replicate messages from leaders.
+            Increasing this value can increase the degree of I/O parallelism 
in the follower broker.
+        </description>
+    </property>
+    <property>
+        <name>replica.high.watermark.checkpoint.interval.ms</name>
+        <value>5000</value>
+        <description>
+            The frequency with which each replica saves its high watermark to 
disk to handle recovery.
+        </description>
+    </property>
+    <property>
+        <name>fetch.purgatory.purge.interval.requests</name>
+        <value>10000</value>
+        <description>The purge interval (in number of requests) of the fetch 
request purgatory.</description>
+    </property>
+    <property>
+        <name>producer.purgatory.purge.interval.requests</name>
+        <value>10000</value>
+        <description>The purge interval (in number of requests) of the 
producer request purgatory.</description>
+    </property>
+    <property>
+        <name>zookeeper.session.timeout.ms</name>
+        <value>30000</value>
+        <description>
+            Zookeeper session timeout.
+            If the server fails to heartbeat to zookeeper within this period 
of time it is considered dead.
+            If you set this too low the server may be falsely considered dead; 
if you set it too high it may take too
+            long to recognize a truly dead server.
+        </description>
+    </property>
+    <property>
+        <name>zookeeper.connection.timeout.ms</name>
+        <value>25000</value>
+        <description>
+            The maximum amount of time that the client waits to establish a 
connection to zookeeper.
+        </description>
+    </property>
+    <property>
+        <name>zookeeper.sync.time.ms</name>
+        <value>2000</value>
+        <description>How far a ZK follower can be behind a ZK 
leader.</description>
+    </property>
+    <property>
+        <name>controlled.shutdown.max.retries</name>
+        <value>3</value>
+        <description>
+            Number of retries to complete the controlled shutdown successfully 
before executing an unclean shutdown.
+        </description>
+    </property>
+    <property>
+        <name>controlled.shutdown.retry.backoff.ms</name>
+        <value>5000</value>
+        <description>Backoff time between shutdown retries.</description>
+    </property>
+    <property>
+        <name>controlled.shutdown.enable</name>
+        <value>true</value>
+        <description>
+            Enable controlled shutdown of the broker.
+            If enabled, the broker will move all leaders on it to some other 
brokers before shutting itself down.
+            This reduces the unavailability window during shutdown.
+        </description>
+    </property>
+    <property>
+        <name>auto.leader.rebalance.enable</name>
+        <value>true</value>
+        <description>
+            Enables auto leader balancing.
+            A background thread checks and triggers leader balance if required 
at regular intervals.
+        </description>
+    </property>
+    <property>
+        <name>num.recovery.threads.per.data.dir</name>
+        <value>1</value>
+        <description>
+            The number of threads per data directory to be used for log 
recovery at startup and flushing at shutdown
+        </description>
+    </property>
+    <property>
+        <name>min.insync.replicas</name>
+        <value>1</value>
+        <description>
+            define the minimum number of replicas in ISR needed to satisfy a 
produce request with required.acks=-1 (or
+            all)
+        </description>
+    </property>
+    <property>
+        <name>leader.imbalance.per.broker.percentage</name>
+        <value>10</value>
+        <description>
+            The ratio of leader imbalance allowed per broker.
+            The controller would trigger a leader balance if it goes above 
this value per broker.
+            The value is specified in percentage.
+        </description>
+    </property>
+    <property>
+        <name>leader.imbalance.check.interval.seconds</name>
+        <value>300</value>
+        <description>
+            The frequency with which the partition re-balance check is 
triggered by the controller
+        </description>
+    </property>
+    <property>
+        <name>offset.metadata.max.bytes</name>
+        <value>4096</value>
+        <description>The maximum size for a metadata entry associated with an 
offset commit</description>
+    </property>
+    <property>
+        <name>offsets.load.buffer.size</name>
+        <value>5242880</value>
+        <description>
+            Batch size for reading from the offsets segments when loading 
offsets into the cache.
+        </description>
+    </property>
+    <property>
+        <name>offsets.topic.replication.factor</name>
+        <value>3</value>
+        <description>
+            The replication factor for the offsets topic (set higher to ensure 
availability).
+            To ensure that the effective replication factor of the offsets 
topic is the configured
+            value, the number of alive brokers has to be at least the 
replication factor at the time of the first
+            request for the offsets topic.
+            If not, either the offsets topic creation will fail or it will get 
a replication factor of min(alive
+            brokers, configured replication factor).
+        </description>
+    </property>
+    <property>
+        <name>offsets.topic.num.partitions</name>
+        <value>50</value>
+        <description>
+            The number of partitions for the offset commit topic (should not 
change after deployment)
+        </description>
+    </property>
+    <property>
+        <name>offsets.topic.segment.bytes</name>
+        <value>104857600</value>
+        <description>
+            The offsets topic segment bytes should be kept relatively small in 
order to facilitate faster log compaction
+            and cache loads
+        </description>
+    </property>
+    <property>
+        <name>offsets.topic.compression.codec</name>
+        <value>0</value>
+        <description>
+            Compression codec for the offsets topic - compression may be used 
to achieve \"atomic\" commits.
+            Default is NoCompression.
+            For Gzip add value 1 , SnappyCompression add value 2, 
LZ4CompressionCodec 3.
+        </description>
+    </property>
+    <property>
+        <name>offsets.retention.minutes</name>
+        <value>86400000</value>
+        <description>Log retention window in minutes for offsets 
topic</description>
+    </property>
+    <property>
+        <name>offsets.retention.check.interval.ms</name>
+        <value>600000</value>
+        <description>Frequency at which to check for stale 
offsets</description>
+    </property>
+    <property>
+        <name>offsets.commit.timeout.ms</name>
+        <value>5000</value>
+        <description>
+            Offset commit will be delayed until all replicas for the offsets 
topic receive the commit or this timeout is
+            reached. This is similar to the producer request timeout.
+        </description>
+    </property>
+    <property>
+        <name>offsets.commit.required.acks</name>
+        <value>-1</value>
+        <description>
+            The required acks before the commit can be accepted.
+            In general, the default (-1) should not be overridden
+        </description>
+    </property>
+    <property>
+        <name>delete.topic.enable</name>
+        <value>true</value>
+        <description>
+            Enables delete topic.
+            Delete topic through the admin tool will have no effect if this 
config is turned off
+        </description>
+    </property>
+    <property>
+        <name>compression.type</name>
+        <value>producer</value>
+        <description>
+            Specify the final compression type for a given topic.
+            This configuration accepts the standard compression codecs 
('gzip', 'snappy', lz4).
+            It additionally accepts 'uncompressed' which is equivalent to no 
compression; and 'producer' which means
+            retain the original compression codec set by the producer.
+        </description>
+    </property>
+    <property>
+        <name>external.kafka.metrics.exclude.prefix</name>
+        
<value>kafka.network.RequestMetrics,kafka.server.DelayedOperationPurgatory,kafka.server.BrokerTopicMetrics.BytesRejectedPerSec</value>
+        <description>Exclude metrics starting with these prefixes from being 
collected.</description>
+    </property>
+    <property>
+        <name>external.kafka.metrics.include.prefix</name>
+        
<value>kafka.network.RequestMetrics.ResponseQueueTimeMs.request.OffsetCommit.98percentile,kafka.network.RequestMetrics.ResponseQueueTimeMs.request.Offsets.95percentile,kafka.network.RequestMetrics.ResponseSendTimeMs.request.Fetch.95percentile,kafka.network.RequestMetrics.RequestsPerSec.request</value>
+        <description>
+            These metrics would be included even if the exclude prefix omits 
them.
+        </description>
+    </property>
+    <property>
+        <name>sasl.enabled.mechanisms</name>
+        <value>GSSAPI</value>
+        <description>
+            The list of SASL mechanisms enabled in the Kafka server.
+            The list may contain any mechanism for which a security provider 
is available.
+            Only GSSAPI is enabled by default.
+        </description>
+    </property>
+    <property>
+        <name>security.inter.broker.protocol</name>
+        <value>PLAINTEXT</value>
+        <description>
+            Security protocol used to communicate between brokers.
+            Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL.
+            It is an error to set this and inter.broker.listener.name 
properties at the same time.
+        </description>
+    </property>
+    <property>
+        <name>sasl.mechanism.inter.broker.protocol</name>
+        <value>GSSAPI</value>
+        <description>SASL mechanism used for inter-broker communication. 
Default is GSSAPI.</description>
+    </property>
+    <property>
+        <name>ssl.client.auth</name>
+        <value>none</value>
+        <description>Configures kafka broker to request client 
authentication.</description>
+    </property>
+    <property>
+        <name>ssl.key.password</name>
+        <value/>
+        <description>The password of private key in the key store 
file.</description>
+    </property>
+    <property>
+        <name>ssl.keystore.location</name>
+        <value/>
+        <description>The location of key store file.</description>
+    </property>
+    <property>
+        <name>ssl.keystore.password</name>
+        <value/>
+        <description>The store password for key store file.</description>
+    </property>
+    <property>
+        <name>ssl.truststore.location</name>
+        <value/>
+        <description>The location of trust store file.</description>
+    </property>
+    <property>
+        <name>ssl.truststore.password</name>
+        <value/>
+        <description>
+            The password for trust store file.
+            If a password is not set access to the truststore is still 
available, but integrity checking is disabled
+        </description>
+    </property>
+    <property>
+        <name>producer.metrics.enable</name>
+        <value>false</value>
+    </property>
+</configuration>
\ No newline at end of file
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-env.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-env.xml
new file mode 100644
index 00000000..e51666ce
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-env.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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.
+-->
+
+<configuration>
+    <property>
+        <name>kafka_log_dir</name>
+        <display-name>Kafka Log directory</display-name>
+        <value>/var/log/kafka</value>
+        <description />
+    </property>
+    <property>
+        <name>kafka_pid_dir</name>
+        <value>/var/run/kafka</value>
+        <display-name>Kafka PID dir</display-name>
+        <description />
+    </property>
+    <property>
+        <name>kafka_user_nofile_limit</name>
+        <value>128000</value>
+        <description>Max open files limit setting for kafka user.</description>
+    </property>
+    <property>
+        <name>kafka_user_nproc_limit</name>
+        <value>65536</value>
+        <description>Max number of processes limit setting for kafka 
user.</description>
+    </property>
+
+    <property>
+        <name>content</name>
+        <display-name>kafka-env template</display-name>
+        <description>This is the freemarker template for kafka-env.sh 
file</description>
+        <value><![CDATA[
+#!/bin/bash
+
+# Set KAFKA specific environment variables here.
+
+# The java implementation to use.
+export JAVA_HOME=${java_home!}
+<#noparse>export PATH=$PATH:${JAVA_HOME}/bin</#noparse>
+export PID_DIR=${kafka_pid_dir!}
+export LOG_DIR=${kafka_log_dir!}
+<#if security_enabled >
+export KAFKA_OPTS="-Djavax.security.auth.useSubjectCredsOnly=false 
{{kafka_kerberos_params}}"
+<#else>
+export KAFKA_OPTS={{kafka_kerberos_params}}
+</#if>
+export CLASSPATH=$CLASSPATH:${kafka_conf_dir}
+]]>
+      </value>
+        <attrs>
+            <type>longtext</type>
+        </attrs>
+    </property>
+</configuration>
\ No newline at end of file
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-log4j.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-log4j.xml
new file mode 100644
index 00000000..f6f76a09
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka-log4j.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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
+  ~
+  ~    https://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.
+-->
+
+<configuration>
+    <property>
+        <name>kafkaLogMaxFileSize</name>
+        <value>256</value>
+        <description>The maximum size of backup file before the log is 
rotated</description>
+        <display-name>Kafka Log: backup file size</display-name>
+    </property>
+    <property>
+        <name>kafkaLogMaxBackupIndex</name>
+        <value>20</value>
+        <description>The number of backup files</description>
+        <display-name>Kafka Log: # of backup files</display-name>
+    </property>
+    <property>
+        <name>controllerLogMaxFileSize</name>
+        <value>256</value>
+        <description>The maximum size of backup file before the log is 
rotated</description>
+        <display-name>Kafka Controller Log: backup file size</display-name>
+    </property>
+    <property>
+        <name>controllerLogMaxBackupIndex</name>
+        <value>20</value>
+        <description>The number of backup files</description>
+        <display-name>Kafka Controller Log: # of backup files</display-name>
+    </property>
+    <property>
+        <name>content</name>
+        <display-name>kafka-log4j template</display-name>
+        <description>Custom log4j.properties</description>
+        <value><![CDATA[
+#
+#
+# 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.
+#
+#
+#
+kafka.logs.dir=logs
+
+log4j.rootLogger=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{ISO8601}] %p %m (%c)%n
+
+log4j.appender.kafkaAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.kafkaAppender.DatePattern='.'yyyy-MM-dd-HH
+<#noparse>log4j.appender.kafkaAppender.File=${kafka.logs.dir}/server.log</#noparse>
+log4j.appender.kafkaAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.kafkaAppender.layout.ConversionPattern=[%d{ISO8601}] %p %m 
(%c)%n
+log4j.appender.kafkaAppender.MaxFileSize = ${kafkaLogMaxFileSize}MB
+log4j.appender.kafkaAppender.MaxBackupIndex = ${kafkaLogMaxBackupIndex}
+
+log4j.appender.stateChangeAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.stateChangeAppender.DatePattern='.'yyyy-MM-dd-HH
+<#noparse>log4j.appender.stateChangeAppender.File=${kafka.logs.dir}/state-change.log</#noparse>
+log4j.appender.stateChangeAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.stateChangeAppender.layout.ConversionPattern=[%d{ISO8601}] %p 
%m (%c)%n
+
+log4j.appender.requestAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.requestAppender.DatePattern='.'yyyy-MM-dd-HH
+<#noparse>log4j.appender.requestAppender.File=${kafka.logs.dir}/kafka-request.log</#noparse>
+log4j.appender.requestAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.requestAppender.layout.ConversionPattern=[%d{ISO8601}] %p %m 
(%c)%n
+
+log4j.appender.cleanerAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.cleanerAppender.DatePattern='.'yyyy-MM-dd-HH
+<#noparse>log4j.appender.cleanerAppender.File=${kafka.logs.dir}/log-cleaner.log</#noparse>
+log4j.appender.cleanerAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.cleanerAppender.layout.ConversionPattern=[%d{ISO8601}] %p %m 
(%c)%n
+
+log4j.appender.controllerAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.controllerAppender.DatePattern='.'yyyy-MM-dd-HH
+<#noparse>log4j.appender.controllerAppender.File=${kafka.logs.dir}/controller.log</#noparse>
+log4j.appender.controllerAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.controllerAppender.layout.ConversionPattern=[%d{ISO8601}] %p %m 
(%c)%n
+log4j.appender.controllerAppender.MaxFileSize = ${controllerLogMaxFileSize}MB
+log4j.appender.controllerAppender.MaxBackupIndex = 
${controllerLogMaxBackupIndex}
+# Turn on all our debugging info
+#log4j.logger.kafka.producer.async.DefaultEventHandler=DEBUG, kafkaAppender
+#log4j.logger.kafka.client.ClientUtils=DEBUG, kafkaAppender
+#log4j.logger.kafka.perf=DEBUG, kafkaAppender
+<#noparse>#log4j.logger.kafka.perf.ProducerPerformance$ProducerThread=DEBUG, 
kafkaAppender</#noparse>
+#log4j.logger.org.I0Itec.zkclient.ZkClient=DEBUG
+log4j.logger.kafka=INFO, kafkaAppender
+<#noparse>log4j.logger.kafka.network.RequestChannel$=WARN, 
requestAppender</#noparse>
+<#noparse>log4j.additivity.kafka.network.RequestChannel$=false</#noparse>
+
+#log4j.logger.kafka.network.Processor=TRACE, requestAppender
+#log4j.logger.kafka.server.KafkaApis=TRACE, requestAppender
+#log4j.additivity.kafka.server.KafkaApis=false
+log4j.logger.kafka.request.logger=WARN, requestAppender
+log4j.additivity.kafka.request.logger=false
+
+log4j.logger.kafka.controller=TRACE, controllerAppender
+log4j.additivity.kafka.controller=false
+
+log4j.logger.kafka.log.LogCleaner=INFO, cleanerAppender
+log4j.additivity.kafka.log.LogCleaner=false
+
+log4j.logger.state.change.logger=TRACE, stateChangeAppender
+log4j.additivity.state.change.logger=false
+]]>
+        </value>
+        <attrs>
+            <type>longtext</type>
+        </attrs>
+    </property>
+</configuration>
\ No newline at end of file
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka.conf.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka.conf.xml
new file mode 100644
index 00000000..48076cd6
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/configuration/kafka.conf.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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.
+-->
+
+<configuration>
+    <property>
+        <name>content</name>
+        <display-name>kafka.conf template</display-name>
+        <description>This is the freemarker template for kafka-env.sh 
file</description>
+        <value><![CDATA[
+#
+# 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.
+#
+
+${kafka_user}    - nofile   ${kafka_user_nofile_limit}
+${kafka_group}   - nproc    ${kafka_user_nproc_limit}
+
+]]>
+        </value>
+        <attrs>
+            <type>longtext</type>
+        </attrs>
+    </property>
+</configuration>
\ No newline at end of file
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/metainfo.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/metainfo.xml
new file mode 100644
index 00000000..562bd378
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/metainfo.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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
+  ~
+  ~    https://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.
+-->
+
+<metainfo>
+    <service>
+        <name>kafka</name>
+        <display-name>Kafka</display-name>
+        <desc>
+            Apache Kafka is an open-source distributed event streaming 
platform used by thousands of companies for
+            high-performance data pipelines, streaming analytics, data 
integration, and mission-critical
+            applications.
+        </desc>
+        <version>2.8.2-1</version>
+        <user>kafka</user>
+        <license>Apache-2.0</license>
+
+        <components>
+            <component>
+                <name>kafka_broker</name>
+                <display-name>Kafka Broker</display-name>
+                <category>server</category>
+                <cardinality>1+</cardinality>
+            </component>
+        </components>
+
+        <package-specifics>
+            <package-specific>
+                <architectures>
+                    <arch>x86_64</arch>
+                    <arch>aarch64</arch>
+                </architectures>
+                <packages>
+                    <package>
+                        <name>kafka-2.8.2-1.tgz</name>
+                        
<checksum>SHA-256:30a2d69ef081813624273d8a406c9b803c5868df998484e27207f85ea217870f</checksum>
+                    </package>
+                </packages>
+            </package-specific>
+        </package-specifics>
+
+        <required-services>
+            <service>zookeeper</service>
+        </required-services>
+
+    </service>
+</metainfo>
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/order.json
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/order.json
new file mode 100644
index 00000000..2f55e86d
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/kafka/order.json
@@ -0,0 +1,8 @@
+{
+  "KAFKA_BROKER-START": [
+    "ZOOKEEPER_SERVER-START"
+  ],
+  "KAFKA_BROKER-RESTART": [
+    "ZOOKEEPER_SERVER-RESTART"
+  ]
+}
\ No newline at end of file
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/configuration/zoo.cfg.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/configuration/zoo.cfg.xml
new file mode 100644
index 00000000..bd09086c
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/configuration/zoo.cfg.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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.
+-->
+
+<configuration>
+    <property>
+        <name>tickTime</name>
+        <value>3000</value>
+        <display-name>Length of single Tick</display-name>
+        <description>
+            The length of a single tick in milliseconds, which is the basic 
time unit used by ZooKeeper
+        </description>
+    </property>
+    <property>
+        <name>initLimit</name>
+        <value>10</value>
+        <display-name>Ticks to allow for sync at Init</display-name>
+        <description>Ticks to allow for sync at Init.</description>
+    </property>
+    <property>
+        <name>syncLimit</name>
+        <value>5</value>
+        <display-name>Ticks to allow for sync at Runtime</display-name>
+        <description>Ticks to allow for sync at Runtime.</description>
+    </property>
+    <property>
+        <name>clientPort</name>
+        <value>2181</value>
+        <display-name>Port for running ZK Server</display-name>
+        <description>Port for running ZK Server.</description>
+    </property>
+    <property>
+        <name>dataDir</name>
+        <value>/hadoop/zookeeper</value>
+        <display-name>ZooKeeper directory</display-name>
+        <description>Data directory for ZooKeeper.</description>
+    </property>
+    <property>
+        <name>autopurge.snapRetainCount</name>
+        <value>30</value>
+        <description>
+            ZooKeeper purge feature retains the autopurge.
+            snapRetainCount most recent snapshots and the corresponding 
transaction logs in the dataDir and dataLogDir
+            respectively and deletes the rest.
+        </description>
+    </property>
+    <property>
+        <name>autopurge.purgeInterval</name>
+        <value>24</value>
+        <description>
+            The time interval in hours for which the purge task has to be 
triggered.
+            Set to a positive integer (1 and above) to enable the auto purging.
+        </description>
+    </property>
+    <property>
+        <name>4lw.commands.whitelist</name>
+        <value>ruok</value>
+        <description>
+            A list of comma separated Four Letter Words commands that user 
wants to use.
+            A valid Four Letter Words command must be put in this list else 
ZooKeeper server will not enable the
+            command.
+            By default the whitelist only contains "srvr" command which 
zkServer.sh uses.
+            The rest of four letter word commands are disabled by default.
+        </description>
+    </property>
+    <property>
+        <name>admin.enableServer</name>
+        <value>true</value>
+        <description>Set to "false" to disable the AdminServer. By default the 
AdminServer is enabled.</description>
+    </property>
+    <property>
+        <name>admin.serverPort</name>
+        <value>9393</value>
+        <description>The port the embedded Jetty server listens on. Defaults 
to 8080.</description>
+    </property>
+    <property>
+        <name>content</name>
+        <description>The port the embedded Jetty server listens on. Defaults 
to 8080.</description>
+        <value><![CDATA[
+<#compress>
+<#list model as key,value>
+    <#if value??>
+        <#if value?string == 'true'>
+            ${key}=true
+        <#elseif value?string == 'false'>
+            ${key}=false
+        <#else>
+            ${key}=${value}
+        </#if>
+    </#if>
+</#list>
+</#compress>
+
+<#noparse>
+<#if zk_server_str?? >
+${zk_server_str}
+</#if>
+
+<#if security_enabled?? && security_enabled >
+authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
+jaasLoginRenew=3600000
+kerberos.removeHostFromPrincipal=true
+kerberos.removeRealmFromPrincipal=true
+</#if>
+</#noparse>
+]]>
+        </value>
+        <attrs>
+            <type>longtext</type>
+        </attrs>
+    </property>
+</configuration>
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/configuration/zookeeper-env.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/configuration/zookeeper-env.xml
new file mode 100644
index 00000000..a947ff69
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/configuration/zookeeper-env.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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.
+-->
+
+<configuration>
+    <property>
+        <name>zookeeper_log_dir</name>
+        <value>/var/log/zookeeper</value>
+        <display-name>ZooKeeper Log Dir</display-name>
+        <description>ZooKeeper Log Dir</description>
+    </property>
+    <property>
+        <name>zookeeper_pid_dir</name>
+        <value>/var/run/zookeeper</value>
+        <display-name>ZooKeeper PID Dir</display-name>
+        <description>ZooKeeper Pid Dir</description>
+    </property>
+    <!-- zookeeper-env.sh -->
+    <property>
+        <name>content</name>
+        <display-name>zookeeper-env template</display-name>
+        <description>This is the freemarker template for zookeeper-env.sh 
file</description>
+        <value><![CDATA[
+export JAVA_HOME=${java_home!}
+export ZOOKEEPER_HOME=${zookeeper_home!}
+export ZOO_LOG_DIR=${zookeeper_log_dir!}
+export ZOOPIDFILE=${zookeeper_pid_file!}
+export SERVER_JVMFLAGS=${SERVER_JVMFLAGS!}
+<#noparse>export JAVA=${JAVA_HOME}/bin/java</#noparse>
+export CLASSPATH=$CLASSPATH:/usr/share/zookeeper/*
+
+<#if securityEnabled?? && securityEnabled >
+export SERVER_JVMFLAGS="$SERVER_JVMFLAGS 
-Djava.security.auth.login.config=${zk_server_jaas_file!}"
+export CLIENT_JVMFLAGS="$CLIENT_JVMFLAGS 
-Djava.security.auth.login.config=${zk_client_jaas_file!} 
-Dzookeeper.sasl.client.username=${zk_principal_user!}"
+</#if>
+]]>
+        </value>
+        <attrs>
+            <type>longtext</type>
+        </attrs>
+    </property>
+</configuration>
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/metainfo.xml
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/metainfo.xml
new file mode 100644
index 00000000..913b576c
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/metainfo.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!--
+  ~ 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
+  ~
+  ~    https://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.
+-->
+
+<metainfo>
+    <service>
+        <name>zookeeper</name>
+        <display-name>ZooKeeper</display-name>
+        <desc>
+            Apache ZooKeeper is an effort to develop and maintain an 
open-source server which enables highly
+            reliable distributed coordination.
+        </desc>
+        <version>3.7.2-1</version>
+        <user>zookeeper</user>
+        <license>Apache-2.0</license>
+
+        <components>
+            <component>
+                <name>zookeeper_server</name>
+                <display-name>ZooKeeper Server</display-name>
+                <category>server</category>
+                <cardinality>1+</cardinality>
+            </component>
+
+            <component>
+                <name>zookeeper_client</name>
+                <display-name>ZooKeeper Client</display-name>
+                <category>client</category>
+                <cardinality>1+</cardinality>
+            </component>
+        </components>
+
+        <package-specifics>
+            <package-specific>
+                <architectures>
+                    <arch>x86_64</arch>
+                    <arch>aarch64</arch>
+                </architectures>
+                <packages>
+                    <package>
+                        <name>zookeeper-3.7.2-1.tar.gz</name>
+                        
<checksum>SHA-256:83e07f914eb3477c77245f4dc44031b82ea6ef1be3691687f469ddc4b8c720bb</checksum>
+                    </package>
+                </packages>
+            </package-specific>
+        </package-specifics>
+    </service>
+</metainfo>
\ No newline at end of file
diff --git 
a/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/order.json
 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/order.json
new file mode 100644
index 00000000..0da47175
--- /dev/null
+++ 
b/bigtop-manager-server/src/test/resources/stacks/bigtop/3.3.0/services/zookeeper/order.json
@@ -0,0 +1,5 @@
+{
+  "ZOOKEEPER_SERVER-STOP": [
+    "KAFKA_BROKER-STOP"
+  ]
+}
\ No newline at end of file

Reply via email to