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

dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new f2263cea7a [INLONG-9932][Manager] Add an agent installer module 
management for agent installation (#9933)
f2263cea7a is described below

commit f2263cea7affb5cf7310913c4e11a2a6b9d4b951
Author: fuweng11 <76141879+fuwen...@users.noreply.github.com>
AuthorDate: Mon Apr 8 19:20:33 2024 +0800

    [INLONG-9932][Manager] Add an agent installer module management for agent 
installation (#9933)
---
 .../common/pojo/agent/installer/ConfigResult.java  |   5 +
 .../{ConfigResult.java => InstallerCode.java}      |  62 +++++----
 .../common/pojo/agent/installer/ModuleConfig.java  |   6 +
 .../inlong/manager/common/enums/ErrorCodeEnum.java |   7 +
 .../inlong/manager/common/enums/ModuleType.java    |  34 +----
 .../manager/dao/entity/ModuleConfigEntity.java     |  42 +++---
 .../manager/dao/entity/PackageConfigEntity.java    |  40 +++---
 .../dao/mapper/ModuleConfigEntityMapper.java       |  45 +++----
 .../dao/mapper/PackageConfigEntityMapper.java      |  45 +++----
 .../main/resources/mappers/ModuleConfigMapper.xml  | 104 +++++++++++++++
 .../main/resources/mappers/PackageConfigMapper.xml | 104 +++++++++++++++
 .../manager/pojo/cluster/ClusterNodeRequest.java   |   3 +
 .../pojo/cluster/agent/AgentClusterNodeDTO.java    |  47 ++++++-
 .../cluster/agent/AgentClusterNodeRequest.java     |  19 +++
 .../cluster/agent/AgentClusterNodeResponse.java    |  19 +++
 .../inlong/manager/pojo/module/ModuleDTO.java      | 110 +++++++++++++++
 .../inlong/manager/pojo/module/ModuleHistory.java  |  29 ++--
 .../manager/pojo/module/ModulePageRequest.java     |  35 +++--
 .../inlong/manager/pojo/module/ModuleRequest.java  |  81 ++++++-----
 .../inlong/manager/pojo/module/ModuleResponse.java |  97 ++++++++++++++
 .../inlong/manager/pojo/module/PackageHistory.java |  30 ++---
 .../manager/pojo/module/PackagePageRequest.java    |  35 +++--
 .../PackageRequest.java}                           |  45 ++++---
 .../manager/pojo/module/PackageResponse.java       |  75 +++++++++++
 .../inlong/manager/service/core/AgentService.java  |   5 +
 .../service/core/impl/AgentServiceImpl.java        | 105 ++++++++++++++-
 .../manager/service/module/ModuleService.java      |  72 ++++++++++
 .../manager/service/module/ModuleServiceImpl.java  | 148 +++++++++++++++++++++
 .../manager/service/module/PackageService.java     |  72 ++++++++++
 .../manager/service/module/PackageServiceImpl.java | 120 +++++++++++++++++
 .../main/resources/h2/apache_inlong_manager.sql    |  37 ++++++
 .../manager-web/sql/apache_inlong_manager.sql      |  38 ++++++
 inlong-manager/manager-web/sql/changes-1.12.0.sql  |  39 ++++++
 .../web/controller/InlongClusterController.java    |   2 +
 .../manager/web/controller/ModuleController.java   |  84 ++++++++++++
 .../manager/web/controller/PackageController.java  |  84 ++++++++++++
 .../controller/openapi/InstallerController.java    |  49 +++++++
 .../src/main/resources/application-dev.properties  |   4 +
 .../src/main/resources/application-prod.properties |   3 +
 .../src/main/resources/application-test.properties |   3 +
 40 files changed, 1696 insertions(+), 288 deletions(-)

diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
index 9e108ae623..fcbbd0f5c1 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
@@ -33,6 +33,11 @@ import java.util.List;
 @AllArgsConstructor
 public class ConfigResult {
 
+    /**
+     * The code of the config result
+     */
+    InstallerCode code;
+
     /**
      * The md5 of the config result
      */
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/InstallerCode.java
similarity index 50%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/InstallerCode.java
index 9e108ae623..60945e4894 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/InstallerCode.java
@@ -17,32 +17,40 @@
 
 package org.apache.inlong.common.pojo.agent.installer;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+public enum InstallerCode {
 
-import java.util.List;
+    SUCCESS(0, "SUCCESS", "Get module config success"),
+    NO_UPDATE(1, "NO_UPDATE", "No update"),
+    UNKNOWN_ERROR(Integer.MAX_VALUE, "UNKNOWN", "Unknown error");
 
-/**
- * The config result pulled by the agent from the manager.
- */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ConfigResult {
-
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+    private final int id;
+    private final String name;
+    private final String desc;
+
+    InstallerCode(int id, String name, String desc) {
+        this.id = id;
+        this.name = name;
+        this.desc = desc;
+    }
+
+    public static InstallerCode valueOf(int value) {
+        for (InstallerCode installerCode : InstallerCode.values()) {
+            if (installerCode.getId() == value) {
+                return installerCode;
+            }
+        }
+        return UNKNOWN_ERROR;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
 
b/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
index c24583c152..c76e42522d 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
+++ 
b/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
@@ -70,4 +70,10 @@ public class ModuleConfig {
      * The state of the module,identify that the module is in a state of 
addition, download, installation, etc
      */
     private ModuleStateEnum state;
+
+    /**
+     * The restart time of the module
+     */
+    private Integer restartTime;
+
 }
\ No newline at end of file
diff --git 
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ErrorCodeEnum.java
 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ErrorCodeEnum.java
index e797335e45..d60ba21add 100644
--- 
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ErrorCodeEnum.java
+++ 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ErrorCodeEnum.java
@@ -156,6 +156,13 @@ public enum ErrorCodeEnum {
     AUDIT_SOURCE_URL_NOT_SUPPORTED(4003, "Audit Source URL '%s' not 
supported"),
 
     TENANT_NOT_EXIST(5001, "Tenant '%s' is not exist"),
+
+    MODULE_NOT_FOUND(6001, "Module does not exist/no operation authority"),
+    MODULE_INFO_INCORRECT(6002, "Module info was incorrect"),
+
+    PACKAGE_NOT_FOUND(7001, "Package does not exist/no operation authority"),
+    PACKAGE_INFO_INCORRECT(7002, "Package info was incorrect")
+
     ;
 
     private final int code;
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ModuleType.java
similarity index 58%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ModuleType.java
index 9e108ae623..8001b20dca 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/enums/ModuleType.java
@@ -15,34 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.util.List;
+package org.apache.inlong.manager.common.enums;
 
 /**
- * The config result pulled by the agent from the manager.
+ * Constant of module type.
  */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ConfigResult {
+public enum ModuleType {
+
+    AGENT,
+    INSTALLER
 
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/entity/ModuleConfigEntity.java
similarity index 59%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/entity/ModuleConfigEntity.java
index 9e108ae623..6329d2faa6 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/entity/ModuleConfigEntity.java
@@ -15,34 +15,30 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.dao.entity;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.Data;
-import lombok.NoArgsConstructor;
 
-import java.util.List;
+import java.io.Serializable;
+import java.util.Date;
 
 /**
- * The config result pulled by the agent from the manager.
+ * Module config entity, including name, type, etc.
  */
 @Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ConfigResult {
+public class ModuleConfigEntity implements Serializable {
 
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+    private static final long serialVersionUID = 1L;
+    private Integer id;
+    private String name;
+    private String type;
+    private Integer packageId;
+    private String extParams;
+    private String version;
+    private Integer isDeleted;
+    private String creator;
+    private String modifier;
+    private Date createTime;
+    private Date modifyTime;
+
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/entity/PackageConfigEntity.java
similarity index 60%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/entity/PackageConfigEntity.java
index 9e108ae623..a07d1cac7e 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/entity/PackageConfigEntity.java
@@ -15,34 +15,30 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.dao.entity;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.Data;
-import lombok.NoArgsConstructor;
 
-import java.util.List;
+import java.io.Serializable;
+import java.util.Date;
 
 /**
- * The config result pulled by the agent from the manager.
+ * Package config entity, including file name, md5, etc.
  */
 @Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ConfigResult {
+public class PackageConfigEntity implements Serializable {
 
-    /**
-     * The md5 of the config result
-     */
+    private static final long serialVersionUID = 1L;
+    private Integer id;
+    private String fileName;
     private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+    private String type;
+    private String downloadUrl;
+    private String storagePath;
+    private Integer isDeleted;
+    private String creator;
+    private String modifier;
+    private Date createTime;
+    private Date modifyTime;
+
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/mapper/ModuleConfigEntityMapper.java
similarity index 58%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/mapper/ModuleConfigEntityMapper.java
index 9e108ae623..cec1881377 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/mapper/ModuleConfigEntityMapper.java
@@ -15,34 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.dao.mapper;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+import org.apache.inlong.manager.dao.entity.ModuleConfigEntity;
+import org.apache.inlong.manager.pojo.module.ModulePageRequest;
+
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
 
 import java.util.List;
 
-/**
- * The config result pulled by the agent from the manager.
- */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ConfigResult {
-
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+@Repository
+public interface ModuleConfigEntityMapper {
+
+    int insert(ModuleConfigEntity record);
+
+    ModuleConfigEntity selectByPrimaryKey(Integer id);
+
+    int updateByIdSelective(ModuleConfigEntity record);
+
+    List<ModuleConfigEntity> selectByCondition(@Param("request") 
ModulePageRequest request);
+
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/mapper/PackageConfigEntityMapper.java
similarity index 58%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/mapper/PackageConfigEntityMapper.java
index 9e108ae623..29ab4e675e 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/mapper/PackageConfigEntityMapper.java
@@ -15,34 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.dao.mapper;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+import org.apache.inlong.manager.dao.entity.PackageConfigEntity;
+import org.apache.inlong.manager.pojo.module.PackagePageRequest;
+
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
 
 import java.util.List;
 
-/**
- * The config result pulled by the agent from the manager.
- */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ConfigResult {
-
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+@Repository
+public interface PackageConfigEntityMapper {
+
+    int insert(PackageConfigEntity record);
+
+    PackageConfigEntity selectByPrimaryKey(Integer id);
+
+    int updateByIdSelective(PackageConfigEntity record);
+
+    List<PackageConfigEntity> selectByCondition(@Param("request") 
PackagePageRequest request);
+
+}
diff --git 
a/inlong-manager/manager-dao/src/main/resources/mappers/ModuleConfigMapper.xml 
b/inlong-manager/manager-dao/src/main/resources/mappers/ModuleConfigMapper.xml
new file mode 100644
index 0000000000..69008a0440
--- /dev/null
+++ 
b/inlong-manager/manager-dao/src/main/resources/mappers/ModuleConfigMapper.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd";>
+<mapper 
namespace="org.apache.inlong.manager.dao.mapper.ModuleConfigEntityMapper">
+    <resultMap id="BaseResultMap" 
type="org.apache.inlong.manager.dao.entity.ModuleConfigEntity">
+        <id column="id" jdbcType="INTEGER" property="id"/>
+        <result column="name" jdbcType="VARCHAR" property="name"/>
+        <result column="type" jdbcType="VARCHAR" property="type"/>
+        <result column="ext_params" jdbcType="VARCHAR" property="extParams"/>
+        <result column="package_id" jdbcType="VARCHAR" property="packageId"/>
+        <result column="version" jdbcType="VARCHAR" property="version"/>
+        <result column="is_deleted" jdbcType="INTEGER" property="isDeleted"/>
+        <result column="creator" jdbcType="VARCHAR" property="creator"/>
+        <result column="modifier" jdbcType="VARCHAR" property="modifier"/>
+        <result column="create_time" jdbcType="TIMESTAMP" 
property="createTime"/>
+        <result column="modify_time" jdbcType="TIMESTAMP" 
property="modifyTime"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id , name, type, ext_params, package_id, version, is_deleted, creator, 
modifier, create_time, modify_time
+    </sql>
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id"
+            
parameterType="org.apache.inlong.manager.dao.entity.ModuleConfigEntity">
+        insert into module_config (id, name, type,
+                                   ext_params, package_id, version,
+                                 creator, modifier)
+        values (#{id, jdbcType=INTEGER}, #{name, jdbcType=VARCHAR},  #{type, 
jdbcType=VARCHAR},
+                #{extParams, jdbcType=VARCHAR}, #{packageId, 
jdbcType=VARCHAR},#{version, jdbcType=VARCHAR},
+                #{creator, jdbcType=VARCHAR}, #{modifier, jdbcType=VARCHAR})
+    </insert>
+
+    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" 
resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from module_config
+        where id = #{id,jdbcType=INTEGER}
+        and is_deleted = 0
+    </select>
+    <update id="updateByIdSelective" 
parameterType="org.apache.inlong.manager.dao.entity.ModuleConfigEntity">
+        update module_config
+        <set>
+            <if test="name != null">
+                name = #{name, jdbcType=VARCHAR},
+            </if>
+            <if test="type != null">
+                type = #{type, jdbcType=VARCHAR},
+            </if>
+            <if test="extParams !=null">
+                ext_params = #{extParams, jdbcType=VARCHAR},
+            </if>
+            <if test="packageId != null">
+                package_id = #{packageId, jdbcType=VARCHAR},
+            </if>
+            <if test="version != null">
+                version = #{version, jdbcType=VARCHAR},
+            </if>
+            <if test="isDeleted != null">
+                is_deleted = #{isDeleted, jdbcType=INTEGER},
+            </if>
+            <if test="modifier != null">
+                modifier = #{modifier, jdbcType=VARCHAR},
+            </if>
+        </set>
+        <where>
+            id = #{id, jdbcType=INTEGER}
+        </where>
+    </update>
+    <select id="selectByCondition"
+            
parameterType="org.apache.inlong.manager.pojo.module.ModulePageRequest"
+            resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from module_config
+        <where>
+            is_deleted = 0
+            <if test="request.keyword != null and request.keyword != ''">
+                and (
+                name like CONCAT('%', #{request.keyword}, '%')
+                )
+            </if>
+            <if test="request.type != null">
+                and type = #{request.type, jdbcType=VARCHAR}
+            </if>
+        </where>
+    </select>
+</mapper>
diff --git 
a/inlong-manager/manager-dao/src/main/resources/mappers/PackageConfigMapper.xml 
b/inlong-manager/manager-dao/src/main/resources/mappers/PackageConfigMapper.xml
new file mode 100644
index 0000000000..fc7421a0aa
--- /dev/null
+++ 
b/inlong-manager/manager-dao/src/main/resources/mappers/PackageConfigMapper.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd";>
+<mapper 
namespace="org.apache.inlong.manager.dao.mapper.PackageConfigEntityMapper">
+    <resultMap id="BaseResultMap" 
type="org.apache.inlong.manager.dao.entity.PackageConfigEntity">
+        <id column="id" jdbcType="INTEGER" property="id"/>
+        <result column="file_name" jdbcType="VARCHAR" property="fileName"/>
+        <result column="md5" jdbcType="VARCHAR" property="md5"/>
+        <result column="type" jdbcType="VARCHAR" property="type"/>
+        <result column="download_url" jdbcType="VARCHAR" 
property="downloadUrl"/>
+        <result column="storage_path" jdbcType="VARCHAR" 
property="storagePath"/>
+        <result column="is_deleted" jdbcType="INTEGER" property="isDeleted"/>
+        <result column="creator" jdbcType="VARCHAR" property="creator"/>
+        <result column="modifier" jdbcType="VARCHAR" property="modifier"/>
+        <result column="create_time" jdbcType="TIMESTAMP" 
property="createTime"/>
+        <result column="modify_time" jdbcType="TIMESTAMP" 
property="modifyTime"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id , file_name, md5, type, download_url, storage_path, is_deleted, 
creator, modifier, create_time, modify_time
+    </sql>
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id"
+            
parameterType="org.apache.inlong.manager.dao.entity.PackageConfigEntity">
+        insert into package_config (id, file_name, md5,
+                                    type, download_url, storage_path,
+                                    creator, modifier)
+        values (#{id, jdbcType=INTEGER}, #{fileName, jdbcType=VARCHAR}, #{md5, 
jdbcType=VARCHAR},
+                #{type, jdbcType=VARCHAR}, #{downloadUrl, jdbcType=VARCHAR}, 
#{storagePath, jdbcType=VARCHAR},
+                #{creator, jdbcType=VARCHAR}, #{modifier, jdbcType=VARCHAR})
+    </insert>
+
+    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" 
resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from package_config
+        where id = #{id,jdbcType=INTEGER}
+        and is_deleted = 0
+    </select>
+    <update id="updateByIdSelective" 
parameterType="org.apache.inlong.manager.dao.entity.PackageConfigEntity">
+        update package_config
+        <set>
+            <if test="fileName != null">
+                file_name = #{fileName, jdbcType=VARCHAR},
+            </if>
+            <if test="md5 != null">
+                md5 = #{md5, jdbcType=VARCHAR},
+            </if>
+            <if test="type != null">
+                type = #{type, jdbcType=VARCHAR},
+            </if>
+            <if test="downloadUrl !=null">
+                download_url = #{downloadUrl, jdbcType=VARCHAR},
+            </if>
+            <if test="storagePath != null">
+                storage_path = #{storagePath, jdbcType=VARCHAR},
+            </if>
+            <if test="isDeleted != null">
+                is_deleted = #{isDeleted, jdbcType=INTEGER},
+            </if>
+            <if test="modifier != null">
+                modifier = #{modifier, jdbcType=VARCHAR},
+            </if>
+        </set>
+        <where>
+            id = #{id, jdbcType=INTEGER}
+        </where>
+    </update>
+    <select id="selectByCondition"
+            
parameterType="org.apache.inlong.manager.pojo.module.ModulePageRequest"
+            resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from package_config
+        <where>
+            is_deleted = 0
+            <if test="request.keyword != null and request.keyword != ''">
+                and (
+                file_name like CONCAT('%', #{request.keyword}, '%')
+                )
+            </if>
+            <if test="request.type != null">
+                and type = #{request.type, jdbcType=VARCHAR}
+            </if>
+        </where>
+    </select>
+</mapper>
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
index 804e8ce068..07960d2304 100644
--- 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
@@ -80,4 +80,7 @@ public class ClusterNodeRequest {
     @ApiModelProperty(value = "Whether to proceed with installation")
     private Boolean isInstall = false;
 
+    @ApiModelProperty(value = "Current user", hidden = true)
+    private String currentUser;
+
 }
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeDTO.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeDTO.java
index d96a4e738c..956e87bdc4 100644
--- 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeDTO.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeDTO.java
@@ -21,17 +21,26 @@ import org.apache.inlong.manager.common.enums.ErrorCodeEnum;
 import org.apache.inlong.manager.common.exceptions.BusinessException;
 import org.apache.inlong.manager.common.util.CommonBeanUtils;
 import org.apache.inlong.manager.common.util.JsonUtils;
+import org.apache.inlong.manager.pojo.module.ModuleHistory;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
+import lombok.Builder.Default;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.validation.constraints.NotNull;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
 /**
  * Agent cluster node info
  */
@@ -42,16 +51,48 @@ import javax.validation.constraints.NotNull;
 @ApiModel("Agent cluster node info")
 public class AgentClusterNodeDTO {
 
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(AgentClusterNodeDTO.class);
+
     @ApiModelProperty(value = "Agent group name")
     private String agentGroup;
 
+    @ApiModelProperty(value = "Module id list")
+    @Default
+    private List<Integer> moduleIdList = new ArrayList<>();
+
+    @ApiModelProperty(value = "Agent restart time")
+    private Integer agentRestartTime = 0;
+
+    @ApiModelProperty(value = "Install restart time")
+    private Integer installRestartTime = 0;
+
+    @ApiModelProperty("History list of module")
+    @Default
+    private List<ModuleHistory> moduleHistoryList = new ArrayList<>();
+
     /**
      * Get the dto instance from the request
      */
     public static AgentClusterNodeDTO getFromRequest(AgentClusterNodeRequest 
request, String extParams) {
-        AgentClusterNodeDTO dto = StringUtils.isNotBlank(extParams)
-                ? AgentClusterNodeDTO.getFromJson(extParams)
-                : new AgentClusterNodeDTO();
+        AgentClusterNodeDTO dto;
+        if (!StringUtils.isNotBlank(extParams)) {
+            return CommonBeanUtils.copyProperties(request, 
AgentClusterNodeDTO::new, true);
+        }
+        dto = AgentClusterNodeDTO.getFromJson(extParams);
+        if (!CollectionUtils.isEqualCollection(request.getModuleIdList(), 
dto.getModuleIdList())) {
+            request.setModuleHistoryList(dto.getModuleHistoryList());
+            List<ModuleHistory> moduleHistoryList = 
request.getModuleHistoryList();
+            if (moduleHistoryList.size() > 10) {
+                moduleHistoryList.remove(moduleHistoryList.size() - 1);
+            }
+            ModuleHistory moduleHistory = ModuleHistory.builder()
+                    .moduleIdList(dto.getModuleIdList())
+                    .modifier(request.getCurrentUser())
+                    .modifyTime(new Date())
+                    .build();
+            moduleHistoryList.add(0, moduleHistory);
+            dto.setModuleHistoryList(moduleHistoryList);
+        }
         return CommonBeanUtils.copyProperties(request, dto, true);
     }
 
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeRequest.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeRequest.java
index 161d7c2b90..c8659efc74 100644
--- 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeRequest.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeRequest.java
@@ -20,13 +20,18 @@ package org.apache.inlong.manager.pojo.cluster.agent;
 import org.apache.inlong.manager.common.enums.ClusterType;
 import org.apache.inlong.manager.common.util.JsonTypeDefine;
 import org.apache.inlong.manager.pojo.cluster.ClusterNodeRequest;
+import org.apache.inlong.manager.pojo.module.ModuleHistory;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder.Default;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Inlong cluster node request for Agent
  */
@@ -40,6 +45,20 @@ public class AgentClusterNodeRequest extends 
ClusterNodeRequest {
     @ApiModelProperty(value = "Agent group name")
     private String agentGroup;
 
+    @ApiModelProperty(value = "Agent restart time")
+    private Integer agentRestartTime = 0;
+
+    @ApiModelProperty(value = "Install restart time")
+    private Integer installRestartTime = 0;
+
+    @ApiModelProperty(value = "Module id list")
+    @Default
+    private List<Integer> moduleIdList = new ArrayList<>();
+
+    @ApiModelProperty("History list of module")
+    @Default
+    private List<ModuleHistory> moduleHistoryList = new ArrayList<>();
+
     public AgentClusterNodeRequest() {
         this.setType(ClusterType.AGENT);
     }
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
index ae71926fa0..645db93073 100644
--- 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
@@ -20,13 +20,18 @@ package org.apache.inlong.manager.pojo.cluster.agent;
 import org.apache.inlong.manager.common.enums.ClusterType;
 import org.apache.inlong.manager.common.util.JsonTypeDefine;
 import org.apache.inlong.manager.pojo.cluster.ClusterNodeResponse;
+import org.apache.inlong.manager.pojo.module.ModuleHistory;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder.Default;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Agent cluster response
  */
@@ -40,6 +45,20 @@ public class AgentClusterNodeResponse extends 
ClusterNodeResponse {
     @ApiModelProperty(value = "Agent group name")
     private String agentGroup;
 
+    @ApiModelProperty(value = "Agent restart time")
+    private Integer agentRestartTime = 0;
+
+    @ApiModelProperty(value = "Install restart time")
+    private Integer installRestartTime = 0;
+
+    @ApiModelProperty(value = "Module id list")
+    @Default
+    private List<Integer> moduleIdList = new ArrayList<>();
+
+    @ApiModelProperty("History list of module")
+    @Default
+    private List<ModuleHistory> moduleHistoryList = new ArrayList<>();
+
     public AgentClusterNodeResponse() {
         this.setType(ClusterType.AGENT);
     }
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleDTO.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleDTO.java
new file mode 100644
index 0000000000..b519140aef
--- /dev/null
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleDTO.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.pojo.module;
+
+import org.apache.inlong.manager.common.enums.ErrorCodeEnum;
+import org.apache.inlong.manager.common.exceptions.BusinessException;
+import org.apache.inlong.manager.common.util.CommonBeanUtils;
+import org.apache.inlong.manager.common.util.JsonUtils;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Builder.Default;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.validation.constraints.NotNull;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Module request.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel("Module info")
+public class ModuleDTO {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ModuleDTO.class);
+
+    @ApiModelProperty("Start command")
+    private String startCommand;
+
+    @ApiModelProperty("Stop command")
+    private String stopCommand;
+
+    @ApiModelProperty("Check command")
+    private String checkCommand;
+
+    @ApiModelProperty("Install command")
+    private String installCommand;
+
+    @ApiModelProperty("Uninstall command")
+    private String uninstallCommand;
+
+    @ApiModelProperty("History list of package")
+    @Default
+    private List<PackageHistory> packageHistoryList = new ArrayList<>();
+
+    /**
+     * Get the dto instance from the request
+     */
+    public static ModuleDTO getFromRequest(ModuleRequest request, String 
extParams, Integer packageId) {
+        if (!StringUtils.isNotBlank(extParams)) {
+            return CommonBeanUtils.copyProperties(request, ModuleDTO::new, 
true);
+        }
+        ModuleDTO dto = ModuleDTO.getFromJson(extParams);
+        if (!Objects.equals(request.getPackageId(), packageId)) {
+            List<PackageHistory> packageHistoryList = 
dto.getPackageHistoryList();
+            if (packageHistoryList.size() > 10) {
+                packageHistoryList.remove(packageHistoryList.size() - 1);
+            }
+            PackageHistory packageHistory = PackageHistory.builder()
+                    .packageId(packageId)
+                    .modifier(request.getCurrentUser())
+                    .modifyTime(new Date())
+                    .build();
+            packageHistoryList.add(0, packageHistory);
+            dto.setPackageHistoryList(packageHistoryList);
+        }
+        return CommonBeanUtils.copyProperties(request, dto, true);
+    }
+
+    /**
+     * Get the dto instance from the JSON string.
+     */
+    public static ModuleDTO getFromJson(@NotNull String extParams) {
+        try {
+            return JsonUtils.parseObject(extParams, ModuleDTO.class);
+        } catch (Exception e) {
+            throw new BusinessException(ErrorCodeEnum.CLUSTER_INFO_INCORRECT,
+                    ErrorCodeEnum.CLUSTER_INFO_INCORRECT.getMessage() + ": " + 
e.getMessage());
+        }
+    }
+
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleHistory.java
similarity index 72%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleHistory.java
index 9e108ae623..d4def7018d 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleHistory.java
@@ -15,34 +15,29 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.pojo.module;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.util.Date;
 import java.util.List;
 
 /**
- * The config result pulled by the agent from the manager.
+ * Module history
  */
 @Data
 @Builder
-@NoArgsConstructor
 @AllArgsConstructor
-public class ConfigResult {
+@NoArgsConstructor
+public class ModuleHistory {
+
+    private List<Integer> moduleIdList;
+    private String modifier;
 
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "GMT+8")
+    private Date modifyTime;
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModulePageRequest.java
similarity index 63%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModulePageRequest.java
index 9e108ae623..855182b72e 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModulePageRequest.java
@@ -15,34 +15,33 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.pojo.module;
 
+import org.apache.inlong.manager.pojo.common.PageRequest;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 
-import java.util.List;
-
 /**
- * The config result pulled by the agent from the manager.
+ * Module config paging query conditions
  */
 @Data
+@EqualsAndHashCode(callSuper = false)
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class ConfigResult {
+@ApiModel("Module paging query request")
+public class ModulePageRequest extends PageRequest {
+
+    @ApiModelProperty(value = "Keywords, used for fuzzy query")
+    private String keyword;
+
+    @ApiModelProperty("Module type")
+    private String type;
 
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleRequest.java
similarity index 51%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
copy to 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleRequest.java
index c24583c152..101fb3cc81 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ModuleConfig.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleRequest.java
@@ -15,59 +15,58 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.pojo.module;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
+import org.apache.inlong.manager.common.validation.UpdateValidation;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
 
 /**
- * The Module config for installer.
+ * Module request.
  */
 @Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class ModuleConfig {
+@ApiModel("Module request")
+public class ModuleRequest {
 
+    @ApiModelProperty(value = "Primary key")
+    @NotNull(groups = UpdateValidation.class)
     private Integer id;
+
+    @ApiModelProperty("Module name")
     private String name;
-    /**
-     * The md5 of the module config
-     */
-    private String md5;
+
+    @ApiModelProperty("Module type")
+    private String type;
+
+    @ApiModelProperty("Module version")
     private String version;
-    /**
-     * Number of processes in one node
-     */
-    private Integer processesNum;
-    /**
-     * The command to start the module
-     */
+
+    @ApiModelProperty("Start command")
     private String startCommand;
-    /**
-     * The command to stop the module
-     */
+
+    @ApiModelProperty("Stop command")
     private String stopCommand;
-    /**
-     * The command to check the processes num of the module
-     */
+
+    @ApiModelProperty("Check command")
     private String checkCommand;
-    /**
-     * The command to install the module
-     */
+
+    @ApiModelProperty("Install command")
     private String installCommand;
-    /**
-     * The command to uninstall the module
-     */
+
+    @ApiModelProperty("Uninstall command")
     private String uninstallCommand;
-    /**
-     * Installation package config
-     */
-    private PackageConfig packageConfig;
-    /**
-     * The state of the module,identify that the module is in a state of 
addition, download, installation, etc
-     */
-    private ModuleStateEnum state;
-}
\ No newline at end of file
+
+    @ApiModelProperty("Package id")
+    private Integer packageId;
+
+    @ApiModelProperty("Extended params")
+    private String extParams;
+
+    @ApiModelProperty(value = "Current user", hidden = true)
+    private String currentUser;
+
+}
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleResponse.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleResponse.java
new file mode 100644
index 0000000000..da390b41be
--- /dev/null
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/ModuleResponse.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.pojo.module;
+
+import org.apache.inlong.manager.common.validation.UpdateValidation;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Builder.Default;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Module response
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel("Module response")
+public class ModuleResponse {
+
+    @ApiModelProperty(value = "Primary key")
+    @NotNull(groups = UpdateValidation.class)
+    private Integer id;
+
+    @ApiModelProperty("Module name")
+    private String name;
+
+    @ApiModelProperty("Module type")
+    private String type;
+
+    @ApiModelProperty("Module version")
+    private String version;
+
+    @ApiModelProperty("Start command")
+    private String startCommand;
+
+    @ApiModelProperty("Stop command")
+    private String stopCommand;
+
+    @ApiModelProperty("Check command")
+    private String checkCommand;
+
+    @ApiModelProperty("Install command")
+    private String installCommand;
+
+    @ApiModelProperty("Uninstall command")
+    private String uninstallCommand;
+
+    @ApiModelProperty("Package id")
+    private Integer packageId;
+
+    @ApiModelProperty("Extended params")
+    private String extParams;
+
+    @ApiModelProperty("History list of package")
+    @Default
+    private List<PackageHistory> packageHistoryList = new ArrayList<>();
+
+    @ApiModelProperty(value = "Name of in creator")
+    private String creator;
+
+    @ApiModelProperty(value = "Name of in modifier")
+    private String modifier;
+
+    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "GMT+8")
+    private Date createTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "GMT+8")
+    private Date modifyTime;
+
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageHistory.java
similarity index 70%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageHistory.java
index 9e108ae623..8346ab38e1 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageHistory.java
@@ -15,34 +15,28 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.pojo.module;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import java.util.List;
+import java.util.Date;
 
 /**
- * The config result pulled by the agent from the manager.
+ * Module history
  */
 @Data
 @Builder
-@NoArgsConstructor
 @AllArgsConstructor
-public class ConfigResult {
+@NoArgsConstructor
+public class PackageHistory {
+
+    private Integer packageId;
+    private String modifier;
 
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "GMT+8")
+    private Date modifyTime;
+}
diff --git 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackagePageRequest.java
similarity index 63%
copy from 
inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
copy to 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackagePageRequest.java
index 9e108ae623..a684d01273 100644
--- 
a/inlong-common/src/main/java/org/apache/inlong/common/pojo/agent/installer/ConfigResult.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackagePageRequest.java
@@ -15,34 +15,33 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.common.pojo.agent.installer;
+package org.apache.inlong.manager.pojo.module;
 
+import org.apache.inlong.manager.pojo.common.PageRequest;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 
-import java.util.List;
-
 /**
- * The config result pulled by the agent from the manager.
+ * Package paging query conditions
  */
 @Data
+@EqualsAndHashCode(callSuper = false)
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class ConfigResult {
+@ApiModel("Package paging query request")
+public class PackagePageRequest extends PageRequest {
+
+    @ApiModelProperty(value = "Keywords, used for fuzzy query")
+    private String keyword;
+
+    @ApiModelProperty("Package type ")
+    private String type;
 
-    /**
-     * The md5 of the config result
-     */
-    private String md5;
-    /**
-     * Number of module
-     */
-    private Integer moduleNum;
-    /**
-     * The list of module config list
-     */
-    private List<ModuleConfig> moduleList;
-}
\ No newline at end of file
+}
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageRequest.java
similarity index 56%
copy from 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
copy to 
inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageRequest.java
index ae71926fa0..4859774b36 100644
--- 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/agent/AgentClusterNodeResponse.java
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageRequest.java
@@ -15,33 +15,40 @@
  * limitations under the License.
  */
 
-package org.apache.inlong.manager.pojo.cluster.agent;
+package org.apache.inlong.manager.pojo.module;
 
-import org.apache.inlong.manager.common.enums.ClusterType;
-import org.apache.inlong.manager.common.util.JsonTypeDefine;
-import org.apache.inlong.manager.pojo.cluster.ClusterNodeResponse;
+import org.apache.inlong.manager.common.validation.UpdateValidation;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
 
 /**
- * Agent cluster response
+ * Package request.
  */
 @Data
-@ToString(callSuper = true)
-@EqualsAndHashCode(callSuper = true)
-@JsonTypeDefine(value = ClusterType.AGENT)
-@ApiModel("Inlong cluster node response for Agent")
-public class AgentClusterNodeResponse extends ClusterNodeResponse {
-
-    @ApiModelProperty(value = "Agent group name")
-    private String agentGroup;
-
-    public AgentClusterNodeResponse() {
-        this.setType(ClusterType.AGENT);
-    }
+@ApiModel("Package request")
+public class PackageRequest {
+
+    @ApiModelProperty(value = "Primary key")
+    @NotNull(groups = UpdateValidation.class)
+    private Integer id;
+
+    @ApiModelProperty(value = "Md5")
+    private String md5;
+
+    @ApiModelProperty("Package type ")
+    private String type;
+
+    @ApiModelProperty(value = "File name")
+    private String fileName;
+
+    @ApiModelProperty(value = "Download url")
+    private String downloadUrl;
+
+    @ApiModelProperty(value = "Storage path")
+    private String storagePath;
 
 }
diff --git 
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageResponse.java
 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageResponse.java
new file mode 100644
index 0000000000..c7aeddbce2
--- /dev/null
+++ 
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/module/PackageResponse.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.pojo.module;
+
+import org.apache.inlong.manager.common.validation.UpdateValidation;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+import java.util.Date;
+
+/**
+ * Package response
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel("Pacekag response")
+public class PackageResponse {
+
+    @ApiModelProperty(value = "Primary key")
+    @NotNull(groups = UpdateValidation.class)
+    private Integer id;
+
+    @ApiModelProperty(value = "Md5")
+    private String md5;
+
+    @ApiModelProperty("Package type ")
+    private String type;
+
+    @ApiModelProperty(value = "File name")
+    private String fileName;
+
+    @ApiModelProperty(value = "Download url")
+    private String downloadUrl;
+
+    @ApiModelProperty(value = "Storage path")
+    private String storagePath;
+
+    @ApiModelProperty(value = "Name of in creator")
+    private String creator;
+
+    @ApiModelProperty(value = "Name of in modifier")
+    private String modifier;
+
+    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "GMT+8")
+    private Date createTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "GMT+8")
+    private Date modifyTime;
+
+}
diff --git 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/AgentService.java
 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/AgentService.java
index 2fd782dbe0..14d63f3c6a 100644
--- 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/AgentService.java
+++ 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/AgentService.java
@@ -20,6 +20,8 @@ package org.apache.inlong.manager.service.core;
 import org.apache.inlong.common.pojo.agent.TaskRequest;
 import org.apache.inlong.common.pojo.agent.TaskResult;
 import org.apache.inlong.common.pojo.agent.TaskSnapshotRequest;
+import org.apache.inlong.common.pojo.agent.installer.ConfigRequest;
+import org.apache.inlong.common.pojo.agent.installer.ConfigResult;
 import 
org.apache.inlong.manager.pojo.cluster.agent.AgentClusterNodeBindGroupRequest;
 
 /**
@@ -59,4 +61,7 @@ public interface AgentService {
      * @return Whether succeed.
      */
     Boolean bindGroup(AgentClusterNodeBindGroupRequest request);
+
+    ConfigResult getConfig(ConfigRequest request);
+
 }
diff --git 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/impl/AgentServiceImpl.java
 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/impl/AgentServiceImpl.java
index 3fa86b02fe..682f130747 100644
--- 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/impl/AgentServiceImpl.java
+++ 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/core/impl/AgentServiceImpl.java
@@ -27,12 +27,18 @@ import org.apache.inlong.common.pojo.agent.DataConfig;
 import org.apache.inlong.common.pojo.agent.TaskRequest;
 import org.apache.inlong.common.pojo.agent.TaskResult;
 import org.apache.inlong.common.pojo.agent.TaskSnapshotRequest;
+import org.apache.inlong.common.pojo.agent.installer.ConfigRequest;
+import org.apache.inlong.common.pojo.agent.installer.ConfigResult;
+import org.apache.inlong.common.pojo.agent.installer.InstallerCode;
+import org.apache.inlong.common.pojo.agent.installer.ModuleConfig;
+import org.apache.inlong.common.pojo.agent.installer.PackageConfig;
 import org.apache.inlong.common.pojo.dataproxy.DataProxyTopicInfo;
 import org.apache.inlong.common.pojo.dataproxy.MQClusterInfo;
 import org.apache.inlong.manager.common.consts.InlongConstants;
 import org.apache.inlong.manager.common.consts.SourceType;
 import org.apache.inlong.manager.common.enums.ClusterType;
 import org.apache.inlong.manager.common.enums.GroupStatus;
+import org.apache.inlong.manager.common.enums.ModuleType;
 import org.apache.inlong.manager.common.enums.SourceStatus;
 import org.apache.inlong.manager.common.exceptions.BusinessException;
 import org.apache.inlong.manager.common.util.CommonBeanUtils;
@@ -42,18 +48,23 @@ import 
org.apache.inlong.manager.dao.entity.InlongClusterEntity;
 import org.apache.inlong.manager.dao.entity.InlongClusterNodeEntity;
 import org.apache.inlong.manager.dao.entity.InlongGroupEntity;
 import org.apache.inlong.manager.dao.entity.InlongStreamEntity;
+import org.apache.inlong.manager.dao.entity.ModuleConfigEntity;
+import org.apache.inlong.manager.dao.entity.PackageConfigEntity;
 import org.apache.inlong.manager.dao.entity.StreamSourceEntity;
 import org.apache.inlong.manager.dao.mapper.DataSourceCmdConfigEntityMapper;
 import org.apache.inlong.manager.dao.mapper.InlongClusterEntityMapper;
 import org.apache.inlong.manager.dao.mapper.InlongClusterNodeEntityMapper;
 import org.apache.inlong.manager.dao.mapper.InlongGroupEntityMapper;
 import org.apache.inlong.manager.dao.mapper.InlongStreamEntityMapper;
+import org.apache.inlong.manager.dao.mapper.ModuleConfigEntityMapper;
+import org.apache.inlong.manager.dao.mapper.PackageConfigEntityMapper;
 import org.apache.inlong.manager.dao.mapper.StreamSourceEntityMapper;
 import org.apache.inlong.manager.pojo.cluster.ClusterPageRequest;
 import 
org.apache.inlong.manager.pojo.cluster.agent.AgentClusterNodeBindGroupRequest;
 import org.apache.inlong.manager.pojo.cluster.agent.AgentClusterNodeDTO;
 import org.apache.inlong.manager.pojo.cluster.pulsar.PulsarClusterDTO;
 import org.apache.inlong.manager.pojo.group.pulsar.InlongPulsarDTO;
+import org.apache.inlong.manager.pojo.module.ModuleDTO;
 import org.apache.inlong.manager.pojo.source.file.FileSourceDTO;
 import org.apache.inlong.manager.pojo.stream.InlongStreamInfo;
 import org.apache.inlong.manager.service.core.AgentService;
@@ -69,6 +80,7 @@ import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.google.gson.Gson;
 import lombok.Getter;
+import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -90,6 +102,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ArrayBlockingQueue;
@@ -129,6 +142,8 @@ public class AgentServiceImpl implements AgentService {
 
     @Getter
     private LoadingCache<TaskRequest, List<StreamSourceEntity>> taskCache;
+    @Getter
+    private LoadingCache<ConfigRequest, ConfigResult> moduleConfigCache;
 
     @Value("${source.update.enabled:false}")
     private Boolean updateTaskTimeoutEnabled;
@@ -147,6 +162,9 @@ public class AgentServiceImpl implements AgentService {
     @Value("${add.task.retention.days:7}")
     private Integer retentionDays;
 
+    @Value("#{${module.name.map:{'agent':1}}}")
+    private Map<String, Integer> moduleNameIdMap = new HashMap<>();
+
     @Autowired
     private StreamSourceEntityMapper sourceMapper;
     @Autowired
@@ -163,6 +181,10 @@ public class AgentServiceImpl implements AgentService {
     private InlongClusterNodeEntityMapper clusterNodeMapper;
     @Autowired
     private SourceOperatorFactory operatorFactory;
+    @Autowired
+    private ModuleConfigEntityMapper moduleConfigEntityMapper;
+    @Autowired
+    private PackageConfigEntityMapper packageConfigEntityMapper;
 
     /**
      * Start the update task
@@ -176,7 +198,15 @@ public class AgentServiceImpl implements AgentService {
         taskCache = Caffeine.newBuilder()
                 .expireAfterWrite(expireTime * 2L, TimeUnit.SECONDS)
                 .build(this::fetchTask);
-
+        LOGGER.debug("start to reload config for installer.");
+        try {
+            moduleConfigCache = Caffeine.newBuilder()
+                    .expireAfterWrite(expireTime * 2L, TimeUnit.SECONDS)
+                    .build(this::loadModuleConfigs);
+        } catch (Throwable t) {
+            LOGGER.error("fail to reload all config for installer ", t);
+        }
+        LOGGER.debug("end to reload config for installer");
         if (updateTaskTimeoutEnabled) {
             ThreadFactory factory = new ThreadFactoryBuilder()
                     .setNameFormat("scheduled-source-timeout-%d")
@@ -403,6 +433,23 @@ public class AgentServiceImpl implements AgentService {
         return true;
     }
 
+    @Override
+    public ConfigResult getConfig(ConfigRequest request) {
+        ConfigResult configResult = moduleConfigCache.get(request);
+        if (configResult == null) {
+            LOGGER.debug(String.format("can not get config result for cluster 
name=%s, ip=%s", request.getClusterName(),
+                    request.getLocalIp()));
+            return null;
+        }
+        if (Objects.equals(request.getMd5(), configResult.getMd5())) {
+            return ConfigResult.builder()
+                    .md5(configResult.getMd5())
+                    .code(InstallerCode.NO_UPDATE)
+                    .build();
+        }
+        return configResult;
+    }
+
     /**
      * Query the tasks that source is waited to be operated.(only clusterName 
and ip matched it can be operated)
      */
@@ -778,4 +825,60 @@ public class AgentServiceImpl implements AgentService {
         return taskLists;
     }
 
+    private ConfigResult loadModuleConfigs(ConfigRequest request) {
+        final String clusterName = request.getClusterName();
+        final String ip = request.getLocalIp();
+        LOGGER.debug("begin to load config for installer = {}", request);
+        Preconditions.expectTrue(StringUtils.isNotBlank(clusterName), "cluster 
name is blank");
+        InlongClusterEntity clusterEntity = 
clusterMapper.selectByNameAndType(clusterName, ClusterType.AGENT);
+        List<InlongClusterNodeEntity> clusterNodeEntityList =
+                clusterNodeMapper.selectByParentIdAndIp(clusterEntity.getId(), 
ip);
+        List<ModuleConfig> configs = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(clusterNodeEntityList)) {
+            AgentClusterNodeDTO dto = 
AgentClusterNodeDTO.getFromJson(clusterNodeEntityList.get(0).getExtParams());
+            configs = getModuleConfigs(dto);
+        }
+        String jsonStr = GSON.toJson(configs);
+        String configMd5 = DigestUtils.md5Hex(jsonStr);
+
+        ConfigResult configResult = 
ConfigResult.builder().moduleList(configs).moduleNum(configs.size())
+                .md5(configMd5)
+                .code(InstallerCode.SUCCESS)
+                .build();
+        LOGGER.info("success load module config, size = {}", 
configResult.getModuleList().size());
+        return configResult;
+    }
+
+    private List<ModuleConfig> getModuleConfigs(AgentClusterNodeDTO dto) {
+        List<Integer> moduleIdList = dto.getModuleIdList();
+        List<ModuleConfig> configs = new ArrayList<>();
+        if (CollectionUtils.isEmpty(moduleIdList)) {
+            return configs;
+        }
+        for (Integer moduleId : moduleIdList) {
+            ModuleConfigEntity moduleConfigEntity = 
moduleConfigEntityMapper.selectByPrimaryKey(moduleId);
+            ModuleConfig moduleConfig = 
CommonBeanUtils.copyProperties(moduleConfigEntity, ModuleConfig::new);
+            
moduleConfig.setId(moduleNameIdMap.getOrDefault(moduleConfigEntity.getName(), 
1));
+            PackageConfigEntity packageConfigEntity =
+                    
packageConfigEntityMapper.selectByPrimaryKey(moduleConfigEntity.getPackageId());
+            moduleConfig
+                    
.setPackageConfig(CommonBeanUtils.copyProperties(packageConfigEntity, 
PackageConfig::new));
+            ModuleDTO moduleDTO = 
JsonUtils.parseObject(moduleConfigEntity.getExtParams(), ModuleDTO.class);
+            moduleConfig = CommonBeanUtils.copyProperties(moduleDTO, 
moduleConfig, true);
+            Integer restartTime = 0;
+            if (Objects.equals(moduleConfigEntity.getType(), 
ModuleType.AGENT.name())) {
+                restartTime = dto.getAgentRestartTime();
+            }
+            if (Objects.equals(moduleConfigEntity.getType(), 
ModuleType.INSTALLER.name())) {
+                restartTime = dto.getInstallRestartTime();
+            }
+            moduleConfig.setRestartTime(restartTime);
+            String moduleStr = GSON.toJson(moduleConfig);
+            String moduleMd5 = DigestUtils.md5Hex(moduleStr);
+            moduleConfig.setMd5(moduleMd5);
+            moduleConfig.setProcessesNum(1);
+            configs.add(moduleConfig);
+        }
+        return configs;
+    }
 }
diff --git 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/ModuleService.java
 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/ModuleService.java
new file mode 100644
index 0000000000..1925186818
--- /dev/null
+++ 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/ModuleService.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.service.module;
+
+import org.apache.inlong.manager.pojo.common.PageResult;
+import org.apache.inlong.manager.pojo.module.ModulePageRequest;
+import org.apache.inlong.manager.pojo.module.ModuleRequest;
+import org.apache.inlong.manager.pojo.module.ModuleResponse;
+import org.apache.inlong.manager.pojo.user.UserInfo;
+
+public interface ModuleService {
+
+    /**
+     * Save inlong module information.
+     *
+     * @param request Inlong module information.
+     * @param operator The name of operator.
+     * @return Id after successful save.
+     */
+    Integer save(ModuleRequest request, String operator);
+
+    /**
+     * Modify inlong module information
+     *
+     * @param request Information that needs to be modified
+     * @param operator Operator's name
+     * @return whether succeed
+     */
+    Boolean update(ModuleRequest request, String operator);
+
+    /**
+     * Get inlong module info based on id
+     *
+     * @param id module id
+     * @param opInfo userinfo of operator
+     * @return detail of module config
+     */
+    ModuleResponse get(Integer id, UserInfo opInfo);
+
+    /**
+     * Paging query module information based on conditions.
+     *
+     * @param request paging request.
+     * @return module list
+     */
+    PageResult<ModuleResponse> listByCondition(ModulePageRequest request);
+
+    /**
+     * Delete the module config by the given id.
+     *
+     * @param id The primary key of the module.
+     * @param operator Operator's name
+     * @return Whether succeed
+     */
+    Boolean delete(Integer id, String operator);
+
+}
diff --git 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/ModuleServiceImpl.java
 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/ModuleServiceImpl.java
new file mode 100644
index 0000000000..92579a2437
--- /dev/null
+++ 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/ModuleServiceImpl.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.service.module;
+
+import org.apache.inlong.manager.common.enums.ErrorCodeEnum;
+import org.apache.inlong.manager.common.exceptions.BusinessException;
+import org.apache.inlong.manager.common.util.CommonBeanUtils;
+import org.apache.inlong.manager.common.util.Preconditions;
+import org.apache.inlong.manager.dao.entity.ModuleConfigEntity;
+import org.apache.inlong.manager.dao.mapper.ModuleConfigEntityMapper;
+import org.apache.inlong.manager.pojo.common.OrderFieldEnum;
+import org.apache.inlong.manager.pojo.common.OrderTypeEnum;
+import org.apache.inlong.manager.pojo.common.PageResult;
+import org.apache.inlong.manager.pojo.module.ModuleDTO;
+import org.apache.inlong.manager.pojo.module.ModulePageRequest;
+import org.apache.inlong.manager.pojo.module.ModuleRequest;
+import org.apache.inlong.manager.pojo.module.ModuleResponse;
+import org.apache.inlong.manager.pojo.user.UserInfo;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.pagehelper.Page;
+import com.github.pagehelper.PageHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Module service layer implementation
+ */
+@Service
+public class ModuleServiceImpl implements ModuleService {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ModuleServiceImpl.class);
+
+    @Autowired
+    private ModuleConfigEntityMapper moduleConfigEntityMapper;
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Override
+    public Integer save(ModuleRequest request, String operator) {
+        LOGGER.info("begin to save module info: {}", request);
+        ModuleConfigEntity moduleConfigEntity = 
CommonBeanUtils.copyProperties(request, ModuleConfigEntity::new);
+        try {
+            ModuleDTO dto = ModuleDTO.getFromRequest(request, 
moduleConfigEntity.getExtParams(),
+                    moduleConfigEntity.getPackageId());
+            String extParams = objectMapper.writeValueAsString(dto);
+            moduleConfigEntity.setExtParams(extParams);
+        } catch (Exception e) {
+            throw new BusinessException(ErrorCodeEnum.MODULE_INFO_INCORRECT,
+                    String.format("serialize extParams of module failure: %s", 
e.getMessage()));
+        }
+        moduleConfigEntity.setCreator(operator);
+        moduleConfigEntity.setModifier(operator);
+        int id = moduleConfigEntityMapper.insert(moduleConfigEntity);
+
+        LOGGER.info("success to save module info: {}", request);
+        return id;
+    }
+
+    @Override
+    public Boolean update(ModuleRequest request, String operator) {
+        LOGGER.info("begin to update module info: {}", request);
+        ModuleConfigEntity moduleConfigEntity = 
moduleConfigEntityMapper.selectByPrimaryKey(request.getId());
+        if (moduleConfigEntity == null) {
+            throw new BusinessException(ErrorCodeEnum.MODULE_NOT_FOUND,
+                    String.format("Module does not exist with id=%s", 
request.getId()));
+        }
+        CommonBeanUtils.copyProperties(request, moduleConfigEntity, true);
+        try {
+            ModuleDTO dto = ModuleDTO.getFromRequest(request, 
moduleConfigEntity.getExtParams(),
+                    moduleConfigEntity.getPackageId());
+            String extParams = objectMapper.writeValueAsString(dto);
+            request.setExtParams(extParams);
+        } catch (Exception e) {
+            throw new BusinessException(ErrorCodeEnum.MODULE_INFO_INCORRECT,
+                    String.format("serialize extParams of module failure: %s", 
e.getMessage()));
+        }
+        CommonBeanUtils.copyProperties(request, moduleConfigEntity, true);
+        moduleConfigEntity.setModifier(operator);
+        moduleConfigEntityMapper.updateByIdSelective(moduleConfigEntity);
+        LOGGER.info("success to update module info: {}", request);
+        return true;
+    }
+
+    @Override
+    public ModuleResponse get(Integer id, UserInfo opInfo) {
+        LOGGER.info("begin to get module info for id = {}", id);
+        ModuleConfigEntity entity = 
moduleConfigEntityMapper.selectByPrimaryKey(id);
+        if (entity == null) {
+            throw new BusinessException(ErrorCodeEnum.MODULE_NOT_FOUND,
+                    String.format("Module config does not exist with id=%s", 
id));
+        }
+        ModuleResponse response = CommonBeanUtils.copyProperties(entity, 
ModuleResponse::new);
+        
CommonBeanUtils.copyProperties(ModuleDTO.getFromJson(entity.getExtParams()), 
response, true);
+        LOGGER.info("begin to get module info for id = {}", id);
+        return response;
+    }
+
+    @Override
+    public PageResult<ModuleResponse> listByCondition(ModulePageRequest 
request) {
+        LOGGER.debug("begin to list source page, request = {}", request);
+        PageHelper.startPage(request.getPageNum(), request.getPageSize());
+        OrderFieldEnum.checkOrderField(request);
+        OrderTypeEnum.checkOrderType(request);
+        Page<ModuleConfigEntity> entityPage =
+                (Page<ModuleConfigEntity>) 
moduleConfigEntityMapper.selectByCondition(request);
+
+        PageResult<ModuleResponse> pageResult = PageResult.fromPage(entityPage)
+                .map(entity -> {
+                    ModuleResponse response = 
CommonBeanUtils.copyProperties(entity, ModuleResponse::new);
+                    
CommonBeanUtils.copyProperties(ModuleDTO.getFromJson(entity.getExtParams()), 
response, true);
+                    return response;
+                });
+        LOGGER.debug("success to list source page, result size {}", 
pageResult.getList().size());
+        return pageResult;
+    }
+
+    @Override
+    public Boolean delete(Integer id, String operator) {
+        LOGGER.info("begin to delete module config by id={}", id);
+        Preconditions.expectNotNull(id, 
ErrorCodeEnum.ID_IS_EMPTY.getMessage());
+        ModuleConfigEntity entity = 
moduleConfigEntityMapper.selectByPrimaryKey(id);
+        Preconditions.expectNotNull(entity, 
ErrorCodeEnum.MODULE_NOT_FOUND.getMessage());
+        entity.setModifier(operator);
+        entity.setIsDeleted(entity.getId());
+        moduleConfigEntityMapper.updateByIdSelective(entity);
+        LOGGER.info("success to delete module config by id: {}", entity);
+        return true;
+    }
+
+}
diff --git 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/PackageService.java
 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/PackageService.java
new file mode 100644
index 0000000000..0f4e0f8b47
--- /dev/null
+++ 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/PackageService.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.service.module;
+
+import org.apache.inlong.manager.pojo.common.PageResult;
+import org.apache.inlong.manager.pojo.module.PackagePageRequest;
+import org.apache.inlong.manager.pojo.module.PackageRequest;
+import org.apache.inlong.manager.pojo.module.PackageResponse;
+import org.apache.inlong.manager.pojo.user.UserInfo;
+
+public interface PackageService {
+
+    /**
+     * Save inlong package information.
+     *
+     * @param request Inlong package information.
+     * @param operator The name of operator.
+     * @return Id after successful save.
+     */
+    Integer save(PackageRequest request, String operator);
+
+    /**
+     * Modify inlong module information
+     *
+     * @param request Information that needs to be modified
+     * @param operator Operator's name
+     * @return whether succeed
+     */
+    Boolean update(PackageRequest request, String operator);
+
+    /**
+     * Get package info based on package id
+     *
+     * @param id package id
+     * @param opInfo userinfo of operator
+     * @return detail of package config
+     */
+    PackageResponse get(Integer id, UserInfo opInfo);
+
+    /**
+     * Paging query package information based on conditions.
+     *
+     * @param request paging request.
+     * @return package list
+     */
+    PageResult<PackageResponse> listByCondition(PackagePageRequest request);
+
+    /**
+     * Delete the package config by the given id.
+     *
+     * @param id The primary key of the package.
+     * @param operator Operator's name
+     * @return Whether succeed
+     */
+    Boolean delete(Integer id, String operator);
+
+}
diff --git 
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/PackageServiceImpl.java
 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/PackageServiceImpl.java
new file mode 100644
index 0000000000..a35a7eedb6
--- /dev/null
+++ 
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/module/PackageServiceImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.service.module;
+
+import org.apache.inlong.manager.common.enums.ErrorCodeEnum;
+import org.apache.inlong.manager.common.exceptions.BusinessException;
+import org.apache.inlong.manager.common.util.CommonBeanUtils;
+import org.apache.inlong.manager.common.util.Preconditions;
+import org.apache.inlong.manager.dao.entity.PackageConfigEntity;
+import org.apache.inlong.manager.dao.mapper.PackageConfigEntityMapper;
+import org.apache.inlong.manager.pojo.common.OrderFieldEnum;
+import org.apache.inlong.manager.pojo.common.OrderTypeEnum;
+import org.apache.inlong.manager.pojo.common.PageResult;
+import org.apache.inlong.manager.pojo.module.PackagePageRequest;
+import org.apache.inlong.manager.pojo.module.PackageRequest;
+import org.apache.inlong.manager.pojo.module.PackageResponse;
+import org.apache.inlong.manager.pojo.user.UserInfo;
+
+import com.github.pagehelper.Page;
+import com.github.pagehelper.PageHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * Package service layer implementation
+ */
+@Service
+public class PackageServiceImpl implements PackageService {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(PackageServiceImpl.class);
+
+    @Autowired
+    private PackageConfigEntityMapper packageConfigEntityMapper;
+
+    @Override
+    public Integer save(PackageRequest request, String operator) {
+        LOGGER.info("begin to save package info: {}", request);
+        PackageConfigEntity packageConfigEntity = 
CommonBeanUtils.copyProperties(request, PackageConfigEntity::new);
+        packageConfigEntity.setCreator(operator);
+        packageConfigEntity.setModifier(operator);
+        int id = packageConfigEntityMapper.insert(packageConfigEntity);
+        LOGGER.info("success to save package info: {}", request);
+        return id;
+    }
+
+    @Override
+    public Boolean update(PackageRequest request, String operator) {
+        LOGGER.info("begin to update package info: {}", request);
+        PackageConfigEntity packageConfigEntity = 
packageConfigEntityMapper.selectByPrimaryKey(request.getId());
+        if (packageConfigEntity == null) {
+            throw new BusinessException(ErrorCodeEnum.PACKAGE_NOT_FOUND,
+                    String.format("Package does not exist with id=%s", 
request.getId()));
+        }
+        CommonBeanUtils.copyProperties(request, packageConfigEntity, true);
+        packageConfigEntity.setModifier(operator);
+        packageConfigEntityMapper.updateByIdSelective(packageConfigEntity);
+        LOGGER.info("success to update package info: {}", request);
+        return true;
+    }
+
+    @Override
+    public PackageResponse get(Integer id, UserInfo opInfo) {
+        LOGGER.info("begin to get package info for id = {}", id);
+        PackageConfigEntity packageConfigEntity = 
packageConfigEntityMapper.selectByPrimaryKey(id);
+        if (packageConfigEntity == null) {
+            throw new BusinessException(ErrorCodeEnum.PACKAGE_NOT_FOUND,
+                    String.format("Package does not exist with id=%s", id));
+        }
+        LOGGER.info("success to get package info for id = {}", id);
+        return CommonBeanUtils.copyProperties(packageConfigEntity, 
PackageResponse::new);
+    }
+
+    @Override
+    public PageResult<PackageResponse> listByCondition(PackagePageRequest 
request) {
+        LOGGER.debug("begin to list package page, request = {}", request);
+        PageHelper.startPage(request.getPageNum(), request.getPageSize());
+        OrderFieldEnum.checkOrderField(request);
+        OrderTypeEnum.checkOrderType(request);
+        Page<PackageConfigEntity> entityPage =
+                (Page<PackageConfigEntity>) 
packageConfigEntityMapper.selectByCondition(request);
+        List<PackageResponse> packageResponseList =
+                CommonBeanUtils.copyListProperties(entityPage, 
PackageResponse::new);
+        PageResult<PackageResponse> pageResult = new 
PageResult<>(packageResponseList, entityPage.getTotal(),
+                entityPage.getPageNum(), entityPage.getPageSize());
+        LOGGER.debug("success to list package page, result size {}", 
pageResult.getList().size());
+        return pageResult;
+    }
+
+    @Override
+    public Boolean delete(Integer id, String operator) {
+        LOGGER.info("begin to delete packeage by id={}", id);
+        Preconditions.expectNotNull(id, 
ErrorCodeEnum.ID_IS_EMPTY.getMessage());
+        PackageConfigEntity entity = 
packageConfigEntityMapper.selectByPrimaryKey(id);
+        Preconditions.expectNotNull(entity, 
ErrorCodeEnum.PACKAGE_NOT_FOUND.getMessage());
+        entity.setModifier(operator);
+        entity.setIsDeleted(entity.getId());
+        packageConfigEntityMapper.updateByIdSelective(entity);
+        LOGGER.info("success to delete package by id: {}", entity);
+        return true;
+    }
+}
diff --git 
a/inlong-manager/manager-test/src/main/resources/h2/apache_inlong_manager.sql 
b/inlong-manager/manager-test/src/main/resources/h2/apache_inlong_manager.sql
index 1a2d28ad2f..8ee1d24145 100644
--- 
a/inlong-manager/manager-test/src/main/resources/h2/apache_inlong_manager.sql
+++ 
b/inlong-manager/manager-test/src/main/resources/h2/apache_inlong_manager.sql
@@ -896,6 +896,43 @@ CREATE TABLE IF NOT EXISTS `tenant_cluster_tag`
     UNIQUE KEY `unique_tenant_inlong_cluster_tag` (`tenant`, `cluster_tag`, 
`is_deleted`)
 );
 
+-- ----------------------------
+-- Table structure for module_config
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `module_config`
+(
+    `id`          int(11)      NOT NULL AUTO_INCREMENT COMMENT 'Incremental 
primary key',
+    `name`        varchar(256) NOT NULL COMMENT 'Module name',
+    `type`        varchar(255) DEFAULT NULL COMMENT 'Module type',
+    `package_id`  int(11)      NOT NULL COMMENT 'Package id',
+    `ext_params`  text                  COMMENT 'Extended params, will be 
saved as JSON string',
+    `version`     varchar(20)  NOT NULL COMMENT 'Version',
+    `is_deleted`  int(11)               DEFAULT '0' COMMENT 'Whether to 
delete, 0: not deleted, > 0: deleted',
+    `creator`     varchar(64)  NOT NULL COMMENT 'Creator name',
+    `modifier`    varchar(64)           DEFAULT NULL COMMENT 'Modifier name',
+    `create_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 
'Create time',
+    `modify_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP COMMENT 'Modify time',
+    PRIMARY KEY (`id`)
+);
+
+-- ----------------------------
+-- Table structure for package_config
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `package_config` (
+    `id`           int(11)      NOT NULL AUTO_INCREMENT COMMENT 'Incremental 
primary key',
+    `md5`          varchar(256) NOT NULL COMMENT 'Md5 of package',
+    `file_name`    varchar(256) NOT NULL COMMENT 'File name',
+    `type`         varchar(255) DEFAULT NULL COMMENT 'Package type',
+    `download_url` varchar(256) NOT NULL COMMENT 'Download url for package',
+    `storage_path` varchar(256) NOT NULL COMMENT 'Storage path for package',
+    `is_deleted`   int(11)               DEFAULT '0' COMMENT 'Whether to 
delete, 0: not deleted, > 0: deleted',
+    `creator`      varchar(64)  NOT NULL COMMENT 'Creator name',
+    `modifier`     varchar(64)           DEFAULT NULL COMMENT 'Modifier name',
+    `create_time`  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 
'Create time',
+    `modify_time`  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP COMMENT 'Modify time',
+    PRIMARY KEY (`id`)
+);
+
 -- ----------------------------
 
 SET FOREIGN_KEY_CHECKS = 1;
diff --git a/inlong-manager/manager-web/sql/apache_inlong_manager.sql 
b/inlong-manager/manager-web/sql/apache_inlong_manager.sql
index 5451516614..38bc6ed8e5 100644
--- a/inlong-manager/manager-web/sql/apache_inlong_manager.sql
+++ b/inlong-manager/manager-web/sql/apache_inlong_manager.sql
@@ -960,5 +960,43 @@ CREATE TABLE IF NOT EXISTS `tenant_cluster_tag`
   DEFAULT CHARSET = utf8mb4 COMMENT ='Tenant cluster tag table';
 
 -- ----------------------------
+-- Table structure for module_config
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `module_config`
+(
+    `id`          int(11)      NOT NULL AUTO_INCREMENT COMMENT 'Incremental 
primary key',
+    `name`        varchar(256) NOT NULL COMMENT 'Module name',
+    `type`        varchar(255) DEFAULT NULL COMMENT 'Module type',
+    `package_id`  int(11)      NOT NULL COMMENT 'Package id',
+    `ext_params`  text                  COMMENT 'Extended params, will be 
saved as JSON string',
+    `version`     varchar(20)  NOT NULL COMMENT 'Version',
+    `is_deleted`  int(11)               DEFAULT '0' COMMENT 'Whether to 
delete, 0: not deleted, > 0: deleted',
+    `creator`     varchar(64)  NOT NULL COMMENT 'Creator name',
+    `modifier`    varchar(64)           DEFAULT NULL COMMENT 'Modifier name',
+    `create_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 
'Create time',
+    `modify_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP COMMENT 'Modify time',
+    PRIMARY KEY (`id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT = 'Module config table'
+
+-- ----------------------------
+-- Table structure for package_config
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `package_config` (
+    `id`           int(11)      NOT NULL AUTO_INCREMENT COMMENT 'Incremental 
primary key',
+    `md5`          varchar(256) NOT NULL COMMENT 'Md5 of package',
+    `file_name`    varchar(256) NOT NULL COMMENT 'File name',
+    `type`         varchar(255) DEFAULT NULL COMMENT 'Package type',
+    `download_url` varchar(256) NOT NULL COMMENT 'Download url for package',
+    `storage_path` varchar(256) NOT NULL COMMENT 'Storage path for package',
+    `is_deleted`   int(11)               DEFAULT '0' COMMENT 'Whether to 
delete, 0: not deleted, > 0: deleted',
+    `creator`      varchar(64)  NOT NULL COMMENT 'Creator name',
+    `modifier`     varchar(64)           DEFAULT NULL COMMENT 'Modifier name',
+    `create_time`  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 
'Create time',
+    `modify_time`  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP COMMENT 'Modify time',
+    PRIMARY KEY (`id`)
+)  ENGINE = InnoDB
+    DEFAULT CHARSET = utf8mb4 COMMENT = 'Package config table'
+-- ----------------------------
 
 SET FOREIGN_KEY_CHECKS = 1;
diff --git a/inlong-manager/manager-web/sql/changes-1.12.0.sql 
b/inlong-manager/manager-web/sql/changes-1.12.0.sql
index a92011164f..b0f2ac8bf6 100644
--- a/inlong-manager/manager-web/sql/changes-1.12.0.sql
+++ b/inlong-manager/manager-web/sql/changes-1.12.0.sql
@@ -29,3 +29,42 @@ CREATE INDEX source_task_map_id_index ON `stream_source` 
(`task_map_id`);
 
 ALTER TABLE `stream_source` CHANGE template_id task_map_id int(11) DEFAULT 
NULL COMMENT 'Id of the task this agent belongs to';
 
+-- ----------------------------
+-- Table structure for module_config
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `module_config`
+(
+    `id`          int(11)      NOT NULL AUTO_INCREMENT COMMENT 'Incremental 
primary key',
+    `name`        varchar(256) NOT NULL COMMENT 'Module name',
+    `type`        varchar(255) DEFAULT NULL COMMENT 'Module type',
+    `package_id`  int(11)      NOT NULL COMMENT 'Package id',
+    `ext_params`  text                  COMMENT 'Extended params, will be 
saved as JSON string',
+    `version`     varchar(20)  NOT NULL COMMENT 'Version',
+    `is_deleted`  int(11)               DEFAULT '0' COMMENT 'Whether to 
delete, 0: not deleted, > 0: deleted',
+    `creator`     varchar(64)  NOT NULL COMMENT 'Creator name',
+    `modifier`    varchar(64)           DEFAULT NULL COMMENT 'Modifier name',
+    `create_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 
'Create time',
+    `modify_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP COMMENT 'Modify time',
+    PRIMARY KEY (`id`)
+) ENGINE = InnoDB
+    DEFAULT CHARSET = utf8mb4 COMMENT = 'Module config table'
+
+-- ----------------------------
+-- Table structure for package_config
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `package_config` (
+    `id`           int(11)      NOT NULL AUTO_INCREMENT COMMENT 'Incremental 
primary key',
+    `md5`          varchar(256) NOT NULL COMMENT 'Md5 of package',
+    `file_name`    varchar(256) NOT NULL COMMENT 'File name',
+    `type`         varchar(255) DEFAULT NULL COMMENT 'Package type',
+    `download_url` varchar(256) NOT NULL COMMENT 'Download url for package',
+    `storage_path` varchar(256) NOT NULL COMMENT 'Storage path for package',
+    `is_deleted`   int(11)               DEFAULT '0' COMMENT 'Whether to 
delete, 0: not deleted, > 0: deleted',
+    `creator`      varchar(64)  NOT NULL COMMENT 'Creator name',
+    `modifier`     varchar(64)           DEFAULT NULL COMMENT 'Modifier name',
+    `create_time`  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 
'Create time',
+    `modify_time`  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP COMMENT 'Modify time',
+    PRIMARY KEY (`id`)
+)  ENGINE = InnoDB
+    DEFAULT CHARSET = utf8mb4 COMMENT = 'Package config table'
+
diff --git 
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
index f0483bf78c..fd95781319 100644
--- 
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
+++ 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
@@ -237,6 +237,7 @@ public class InlongClusterController {
     @OperationLog(operation = OperationType.CREATE, operationTarget = 
OperationTarget.CLUSTER)
     public Response<Integer> saveNode(@Validated @RequestBody 
ClusterNodeRequest request) {
         String currentUser = LoginUserUtils.getLoginUser().getName();
+        request.setCurrentUser(currentUser);
         return Response.success(clusterService.saveNode(request, currentUser));
     }
 
@@ -273,6 +274,7 @@ public class InlongClusterController {
     @ApiOperation(value = "Update cluster node")
     public Response<Boolean> updateNode(@Validated(UpdateValidation.class) 
@RequestBody ClusterNodeRequest request) {
         String username = LoginUserUtils.getLoginUser().getName();
+        request.setCurrentUser(username);
         return Response.success(clusterService.updateNode(request, username));
     }
 
diff --git 
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/ModuleController.java
 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/ModuleController.java
new file mode 100644
index 0000000000..bd001bdbf8
--- /dev/null
+++ 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/ModuleController.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.web.controller;
+
+import org.apache.inlong.manager.common.validation.SaveValidation;
+import org.apache.inlong.manager.common.validation.UpdateValidation;
+import org.apache.inlong.manager.pojo.common.PageResult;
+import org.apache.inlong.manager.pojo.common.Response;
+import org.apache.inlong.manager.pojo.module.ModulePageRequest;
+import org.apache.inlong.manager.pojo.module.ModuleRequest;
+import org.apache.inlong.manager.pojo.module.ModuleResponse;
+import org.apache.inlong.manager.pojo.user.LoginUserUtils;
+import org.apache.inlong.manager.service.module.ModuleService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Inlong module control layer
+ */
+@RestController
+@RequestMapping("/api")
+@Api(tags = "Inlong-Module-API")
+public class ModuleController {
+
+    @Autowired
+    private ModuleService moduleService;
+
+    @RequestMapping(value = "/module/save", method = RequestMethod.POST)
+    @ApiOperation(value = "Save inlong module")
+    public Response<Integer> save(@Validated(SaveValidation.class) 
@RequestBody ModuleRequest request) {
+        String operator = LoginUserUtils.getLoginUser().getName();
+        return Response.success(moduleService.save(request, operator));
+    }
+
+    @RequestMapping(value = "/module/update", method = RequestMethod.POST)
+    @ApiOperation(value = "Update inlong module")
+    public Response<Boolean> update(@Validated(UpdateValidation.class) 
@RequestBody ModuleRequest request) {
+        return Response.success(moduleService.update(request, 
LoginUserUtils.getLoginUser().getName()));
+    }
+
+    @RequestMapping(value = "/module/get/{id}", method = RequestMethod.GET)
+    @ApiOperation(value = "Get module config")
+    @ApiImplicitParam(name = "id", dataTypeClass = Integer.class, required = 
true)
+    public Response<ModuleResponse> get(@PathVariable Integer id) {
+        return Response.success(moduleService.get(id, 
LoginUserUtils.getLoginUser()));
+    }
+
+    @RequestMapping(value = "/module/list", method = RequestMethod.POST)
+    @ApiOperation(value = "List module by paginating")
+    public Response<PageResult<ModuleResponse>> listByCondition(@RequestBody 
ModulePageRequest request) {
+        return Response.success(moduleService.listByCondition(request));
+    }
+
+    @RequestMapping(value = "/module/delete/{id}", method = 
RequestMethod.DELETE)
+    @ApiOperation(value = "Delete module config")
+    public Response<Boolean> delete(@PathVariable Integer id) {
+        return Response.success(moduleService.delete(id, 
LoginUserUtils.getLoginUser().getName()));
+    }
+
+}
\ No newline at end of file
diff --git 
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/PackageController.java
 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/PackageController.java
new file mode 100644
index 0000000000..5db84705df
--- /dev/null
+++ 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/PackageController.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.web.controller;
+
+import org.apache.inlong.manager.common.validation.SaveValidation;
+import org.apache.inlong.manager.common.validation.UpdateValidation;
+import org.apache.inlong.manager.pojo.common.PageResult;
+import org.apache.inlong.manager.pojo.common.Response;
+import org.apache.inlong.manager.pojo.module.PackagePageRequest;
+import org.apache.inlong.manager.pojo.module.PackageRequest;
+import org.apache.inlong.manager.pojo.module.PackageResponse;
+import org.apache.inlong.manager.pojo.user.LoginUserUtils;
+import org.apache.inlong.manager.service.module.PackageService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Inlong package control layer
+ */
+@RestController
+@RequestMapping("/api")
+@Api(tags = "Inlong-Package-API")
+public class PackageController {
+
+    @Autowired
+    private PackageService packageService;
+
+    @RequestMapping(value = "/package/save", method = RequestMethod.POST)
+    @ApiOperation(value = "Save inlong package")
+    public Response<Integer> save(@Validated(SaveValidation.class) 
@RequestBody PackageRequest request) {
+        String operator = LoginUserUtils.getLoginUser().getName();
+        return Response.success(packageService.save(request, operator));
+    }
+
+    @RequestMapping(value = "/package/update", method = RequestMethod.POST)
+    @ApiOperation(value = "Update inlong package")
+    public Response<Boolean> update(@Validated(UpdateValidation.class) 
@RequestBody PackageRequest request) {
+        return Response.success(packageService.update(request, 
LoginUserUtils.getLoginUser().getName()));
+    }
+
+    @RequestMapping(value = "/package/get/{id}", method = RequestMethod.GET)
+    @ApiOperation(value = "Get package config")
+    @ApiImplicitParam(name = "id", dataTypeClass = Integer.class, required = 
true)
+    public Response<PackageResponse> get(@PathVariable Integer id) {
+        return Response.success(packageService.get(id, 
LoginUserUtils.getLoginUser()));
+    }
+
+    @RequestMapping(value = "/package/list", method = RequestMethod.POST)
+    @ApiOperation(value = "List package by paginating")
+    public Response<PageResult<PackageResponse>> listByCondition(@RequestBody 
PackagePageRequest request) {
+        return Response.success(packageService.listByCondition(request));
+    }
+
+    @RequestMapping(value = "/package/delete/{id}", method = 
RequestMethod.DELETE)
+    @ApiOperation(value = "Delete package config")
+    public Response<Boolean> delete(@PathVariable Integer id) {
+        return Response.success(packageService.delete(id, 
LoginUserUtils.getLoginUser().getName()));
+    }
+
+}
\ No newline at end of file
diff --git 
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/openapi/InstallerController.java
 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/openapi/InstallerController.java
new file mode 100644
index 0000000000..530074ce36
--- /dev/null
+++ 
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/openapi/InstallerController.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.web.controller.openapi;
+
+import org.apache.inlong.common.pojo.agent.installer.ConfigRequest;
+import org.apache.inlong.common.pojo.agent.installer.ConfigResult;
+import org.apache.inlong.manager.pojo.common.Response;
+import org.apache.inlong.manager.service.core.AgentService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Installer controller.
+ */
+@RestController
+@RequestMapping("/openapi")
+@Api(tags = "Open-Installer-API")
+public class InstallerController {
+
+    @Autowired
+    private AgentService agentService;
+
+    @PostMapping("/installer/getConfig")
+    @ApiOperation(value = "Get config for installer")
+    public Response<ConfigResult> getConfig(@RequestBody ConfigRequest 
request) {
+        return Response.success(agentService.getConfig(request));
+    }
+}
diff --git 
a/inlong-manager/manager-web/src/main/resources/application-dev.properties 
b/inlong-manager/manager-web/src/main/resources/application-dev.properties
index 8a9032d5ec..ec773d0442 100644
--- a/inlong-manager/manager-web/src/main/resources/application-dev.properties
+++ b/inlong-manager/manager-web/src/main/resources/application-dev.properties
@@ -117,3 +117,7 @@ metrics.audit.proxy.hosts=127.0.0.1:10081
 
 # tencent cloud log service endpoint, The Operator cls resource by it
 cls.manager.endpoint=127.0.0.1
+
+# The mapping relationship between module name and module id
+module.name.map={'inlong-agent':1,'agent-installer':2,'temp-cmd':3}
+
diff --git 
a/inlong-manager/manager-web/src/main/resources/application-prod.properties 
b/inlong-manager/manager-web/src/main/resources/application-prod.properties
index 835822bf84..920eec020d 100644
--- a/inlong-manager/manager-web/src/main/resources/application-prod.properties
+++ b/inlong-manager/manager-web/src/main/resources/application-prod.properties
@@ -116,3 +116,6 @@ metrics.audit.proxy.hosts=127.0.0.1:10081
 
 # tencent cloud log service endpoint, The Operator cls resource by it
 cls.manager.endpoint=127.0.0.1
+
+# The mapping relationship between module name and module id
+module.name.map={'inlong-agent':1,'agent-installer':2,'temp-cmd':3}
diff --git 
a/inlong-manager/manager-web/src/main/resources/application-test.properties 
b/inlong-manager/manager-web/src/main/resources/application-test.properties
index 8a9032d5ec..c8323e5592 100644
--- a/inlong-manager/manager-web/src/main/resources/application-test.properties
+++ b/inlong-manager/manager-web/src/main/resources/application-test.properties
@@ -117,3 +117,6 @@ metrics.audit.proxy.hosts=127.0.0.1:10081
 
 # tencent cloud log service endpoint, The Operator cls resource by it
 cls.manager.endpoint=127.0.0.1
+
+# The mapping relationship between module name and module id
+module.name.map={'inlong-agent':1,'agent-installer':2,'temp-cmd':3}

Reply via email to