[Feature][Task Plugin] Support hive cli task plugin (#11651)

* Add hive cli task plugin

* Add docs for hive-cli task plugin

* Fix hive-cli task related front-end nits and checkstyle

* Fix exception handling logic
This commit is contained in:
Eric Gao 2022-08-30 10:45:51 +08:00 committed by GitHub
parent 71eeab3d98
commit 4a37345436
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 1413 additions and 396 deletions

View File

@ -161,6 +161,10 @@ export default {
title: 'Jupyter',
link: '/en-us/docs/dev/user_doc/guide/task/jupyter.html',
},
{
title: 'Hive CLI',
link: '/en-us/docs/dev/user_doc/guide/task/hive-cli.html',
},
{
title: 'Kubernetes',
link: '/en-us/docs/dev/user_doc/guide/task/kubernetes.html',
@ -781,6 +785,10 @@ export default {
title: 'Jupyter',
link: '/zh-cn/docs/dev/user_doc/guide/task/jupyter.html',
},
{
title: 'Hive CLI',
link: '/zh-cn/docs/dev/user_doc/guide/task/hive-cli.html',
},
{
title: 'Kubernetes',
link: '/zh-cn/docs/dev/user_doc/guide/task/kubernetes.html',

View File

@ -0,0 +1,56 @@
# Hive CLI
## Overview
Use `Hive Cli Task` to create a `Hive Cli` type task and execute hive SQL from scripts or files.
The workers run `hive -e` to execute hive sql from scripts or `hive -f` to execute from files in `Resource Center`.
## Hive CLI Task vs SQL Task With Hive Datasource
In DolphinScheduler, we have both `Hive CLI Task` and `SQL Task With Hive Datasource` for different scenarios.
You could choose between these two based on your needs.
- The `Hive CLI` task plugin connects directly to `HDFS` and the `Hive Metastore` for hive task executions,
which requires your workers to have access to those services, such as related `Hive` libs, `Hive` and `HDFS` configuration files.
However, `Hive CLI Task` provides better stability for scheduling in production.
- `SQL Task With Hive Datasource` does not require access to `Hive` libs, `Hive` and
`HDFS` configuration files and supports `Kerberos` for authentication. However, you may encounter `HiveServer2` failures
if your hive sql task scheduling puts significant pressure on it.
## Create Task
- Click `Project Management-Project Name-Workflow Definition`, and click the `Create Workflow` button to enter the DAG editing page.
- Drag <img src="../../../../img/tasks/icons/hivecli.png" width="15"/> from the toolbar to the canvas.
## Task Parameters
| **Parameter** | **Description** |
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Node Name | The name of the task. Node names within the same workflow must be unique. |
| Run Flag | Indicating whether to schedule the task. If you do not need to execute the task, you can turn on the `Prohibition execution` switch. |
| Description | Describing the function of this node. |
| Task Priority | When the number of the worker threads is insufficient, the worker executes task according to the priority. When two tasks have the same priority, the worker will execute them in `first come first served` fashion. |
| Worker Group | Machines which execute the tasks. If you choose `default`, scheduler will send the task to a random worker. |
| Task Group Name | Resource group of tasks. It will not take effect if not configured. |
| Environment Name | Environment to execute the task. |
| Number of Failed Retries | The number of task retries for failures. You could select it by drop-down menu or fill it manually. |
| Failure Retry Interval | Interval of task retries for failures. You could select it by drop-down menu or fill it manually. |
| CPU Quota | Assign the specified CPU time quota to the task executed. Takes a percentage value. Default -1 means unlimited. For example, the full CPU load of one core is 100%, and that of 16 cores is 1600%. You could configure it by [task.resource.limit.state](../../architecture/configuration.md). |
| Max Memory | Assign the specified max memory to the task executed. Exceeding this limit will trigger oom to be killed and will not automatically retry. Takes an MB value. Default -1 means unlimited. You could configure it by [task.resource.limit.state](../../architecture/configuration.md). |
| Timeout Alarm | Alarm for task timeout. When the task exceeds the "timeout threshold", an alarm email will send. |
| Hive Cli Task Execution Type | The type of hive cli task execution, choose either `FROM_SCRIPT` or `FROM_FILE`. |
| Hive SQL Script | If you choose `FROM_SCRIPT` for `Hive Cli Task Execution Type`, you need to fill in your SQL script. |
| Hive Cli Options | Extra options for hive cli, such as `--verbose` |
| Resources | If you choose `FROM_FILE` for `Hive Cli Task Execution Type`, you need to select your SQL file. |
## Task Example
### Hive Cli Task Example
This example below illustrates how to create a `Hive CLI` task node and execute hive SQL from script:
![demo-hive-cli-from-script](../../../../img/tasks/demo/hive_cli_from_script.png)
This example below illustrates how to create a `Hive CLI` task node and execute hive SQL from file:
![demo-hive-cli-from-file](../../../../img/tasks/demo/hive_cli_from_file.png)

View File

@ -0,0 +1,57 @@
# Hive CLI
## 综述
使用`Hive Cli任务插件`创建`Hive Cli`类型的任务执行SQL脚本语句或者SQL任务文件。
执行任务的worker会通过`hive -e`命令执行hive SQL脚本语句或者通过`hive -f`命令执行`资源中心`中的hive SQL文件。
## Hive CLI任务 VS 连接Hive数据源的SQL任务
在DolphinScheduler中我们有`Hive CLI任务插件`和`使用Hive数据源的SQL插件`提供用户在不同场景下使用,您可以根据需要进行选择。
- `Hive CLI任务插件`直接连接`HDFS`和`Hive Metastore`来执行hive类型的任务所以需要能够访问到对应的服务。
执行任务的worker节点需要有相应的`Hive` jar包以及`Hive`和`HDFS`的配置文件。
但是在生产调度中,`Hive CLI任务插件`能够提供更可靠的稳定性。
- `使用Hive数据源的SQL插件`不需要您在worker节点上有相应的`Hive` jar包以及`Hive`和`HDFS`的配置文件,而且支持 `Kerberos`认证。
但是在生产调度中,若调度压力很大,使用这种方式可能会遇到`HiveServer2`服务过载失败等问题。
## 创建任务
- 点击项目管理-项目名称-工作流定义,点击"创建工作流"按钮进入DAG编辑页面。
- 工具栏中拖动 <img src="../../../../img/tasks/icons/hivecli.png" width="15"/> 到画板中,即可完成创建。
## 任务参数
- 前置任务:选择当前任务的前置任务,会将被选择的前置任务设置为当前任务的上游。
| **任务参数** | **描述** |
|---------------|-------------------------------------------------------------------------------------------------------------------------------------|
| 任务名称 | 设置任务的名称。一个工作流定义中的节点名称是唯一的。 |
| 运行标志 | 标识这个节点是否需要正常调度,如果不需要执行,可以打开禁止执行开关。 |
| 描述 | 描述该节点的功能。 |
| 任务优先级 | worker线程数不足时根据优先级从高到低依次执行优先级一样时根据先进先出原则执行。 |
| Worker分组 | 任务分配给worker组的机器机执行选择Default会随机选择一台worker机执行。 |
| 任务组名称 | 任务资源组,如果没有配置的话就不会生效。 |
| 环境名称 | 配置任务执行的环境。 |
| 失败重试次数 | 任务失败重新提交的次数,支持下拉和手填。 |
| 失败重试间隔 | 任务失败重新提交任务的时间间隔,支持下拉和手填。 |
| CPU 配额 | 为执行的任务分配指定的CPU时间配额单位百分比默认-1代表不限制例如1个核心的CPU满载是100%16个核心的是1600%。 [task.resource.limit.state](../../architecture/configuration.md) |
| 最大内存 | 为执行的任务分配指定的内存大小超过会触发OOM被Kill同时不会进行自动重试单位MB默认-1代表不限制。这个功能由 [task.resource.limit.state](../../architecture/configuration.md) 控制。 |
| 超时告警 | 勾选超时告警、超时失败,当任务超过"超时时长"后,会发送告警邮件并且任务执行失败.这个功能由 [task.resource.limit.state](../../architecture/configuration.md) 控制。 |
| Hive Cli 任务类型 | Hive Cli任务执行方式可以选择`FROM_SCRIPT`或者`FROM_FILE`。 |
| Hive SQL 脚本 | 手动填入您的Hive SQL脚本语句。 |
| Hive Cli 选项 | Hive Cli的其他选项如`--verbose`。 |
| 资源 | 如果您选择`FROM_FILE`作为Hive Cli任务类型您需要在资源中选择Hive SQL文件。 |
## 任务样例
### Hive CLI任务样例
下面的样例演示了如何使用`Hive CLI`任务节点执行Hive SQL脚本语句
![demo-hive-cli-from-script](../../../../img/tasks/demo/hive_cli_from_script.png)
下面的样例演示了如何使用`Hive CLI`任务节点从资源中心的Hive SQL
![demo-hive-cli-from-file](../../../../img/tasks/demo/hive_cli_from_file.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

View File

@ -15,15 +15,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>dolphinscheduler-task-plugin</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-task-plugin</artifactId>
<version>dev-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dolphinscheduler-task-all</artifactId>
@ -201,6 +200,12 @@
<artifactId>dolphinscheduler-task-pytorch</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-task-hivecli</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
</project>

View File

@ -0,0 +1,40 @@
<?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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-task-plugin</artifactId>
<version>dev-SNAPSHOT</version>
</parent>
<artifactId>dolphinscheduler-task-hivecli</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-task-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,33 @@
/*
* 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.dolphinscheduler.plugin.task.hivecli;
import lombok.experimental.UtilityClass;
@UtilityClass
public class HiveCliConstants {
public static final String TYPE_SCRIPT = "SCRIPT";
public static final String TYPE_FILE = "FILE";
public static final String HIVE_CLI_EXECUTE_FILE = "hive -f";
public static final String HIVE_CLI_EXECUTE_SCRIPT = "hive -e \"%s\"";
}

View File

@ -0,0 +1,59 @@
/*
* 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.dolphinscheduler.plugin.task.hivecli;
import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo;
import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import java.util.List;
import lombok.Data;
@Data
public class HiveCliParameters extends AbstractParameters {
private String hiveSqlScript;
private String hiveCliTaskExecutionType;
private String hiveCliOptions;
private List<ResourceInfo> resourceList;
@Override
public boolean checkParameters() {
if (!StringUtils.isNotEmpty(hiveCliTaskExecutionType)) {
return false;
}
if (HiveCliConstants.TYPE_SCRIPT.equals(hiveCliTaskExecutionType)) {
return StringUtils.isNotEmpty(hiveSqlScript);
} else if (HiveCliConstants.TYPE_FILE.equals(hiveCliTaskExecutionType)) {
return (resourceList != null) && (resourceList.size() > 0);
} else {
return false;
}
}
@Override
public List<ResourceInfo> getResourceFilesList() {
return this.resourceList;
}
}

View File

@ -0,0 +1,133 @@
/*
* 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.dolphinscheduler.plugin.task.hivecli;
import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.EXIT_CODE_FAILURE;
import org.apache.dolphinscheduler.plugin.task.api.AbstractTaskExecutor;
import org.apache.dolphinscheduler.plugin.task.api.ShellCommandExecutor;
import org.apache.dolphinscheduler.plugin.task.api.TaskException;
import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext;
import org.apache.dolphinscheduler.plugin.task.api.model.Property;
import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo;
import org.apache.dolphinscheduler.plugin.task.api.model.TaskResponse;
import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters;
import org.apache.dolphinscheduler.plugin.task.api.parser.ParamUtils;
import org.apache.dolphinscheduler.plugin.task.api.parser.ParameterUtils;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HiveCliTask extends AbstractTaskExecutor {
private HiveCliParameters hiveCliParameters;
private final ShellCommandExecutor shellCommandExecutor;
private final TaskExecutionContext taskExecutionContext;
public HiveCliTask(TaskExecutionContext taskExecutionContext) {
super(taskExecutionContext);
this.taskExecutionContext = taskExecutionContext;
this.shellCommandExecutor = new ShellCommandExecutor(this::logHandle,
taskExecutionContext,
logger);
}
@Override
public void init() {
logger.info("hiveCli task params {}", taskExecutionContext.getTaskParams());
hiveCliParameters = JSONUtils.parseObject(taskExecutionContext.getTaskParams(), HiveCliParameters.class);
if (!hiveCliParameters.checkParameters()) {
throw new TaskException("hiveCli task params is not valid");
}
}
@Override
public void handle() throws TaskException {
try {
final TaskResponse taskResponse = shellCommandExecutor.run(buildCommand());
setExitStatusCode(taskResponse.getExitStatusCode());
setAppIds(taskResponse.getAppIds());
setProcessId(taskResponse.getProcessId());
setVarPool(shellCommandExecutor.getVarPool());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.error("The current HiveCLI Task has been interrupted", e);
setExitStatusCode(EXIT_CODE_FAILURE);
throw new TaskException("The current HiveCLI Task has been interrupted", e);
} catch (Exception e) {
logger.error("hiveCli task failure", e);
setExitStatusCode(EXIT_CODE_FAILURE);
throw new TaskException("run hiveCli task error", e);
}
}
protected String buildCommand() {
final List<String> args = new ArrayList<>();
final String type = hiveCliParameters.getHiveCliTaskExecutionType();
// TODO: make sure type is not unknown
if (HiveCliConstants.TYPE_FILE.equals(type)) {
args.add(HiveCliConstants.HIVE_CLI_EXECUTE_FILE);
final List<ResourceInfo> resourceInfos = hiveCliParameters.getResourceList();
if (resourceInfos.size() > 1) {
logger.warn("more than 1 files detected, use the first one by default");
}
args.add(StringUtils.stripStart(resourceInfos.get(0).getResourceName(), "/"));
} else {
final String script = hiveCliParameters.getHiveSqlScript();
args.add(String.format(HiveCliConstants.HIVE_CLI_EXECUTE_SCRIPT, script));
}
final String hiveCliOptions = hiveCliParameters.getHiveCliOptions();
if (StringUtils.isNotEmpty(hiveCliOptions)) {
args.add(hiveCliOptions);
}
final Map<String, Property> paramsMap = taskExecutionContext.getPrepareParamsMap();
final String command =
ParameterUtils.convertParameterPlaceholders(String.join(" ", args), ParamUtils.convert(paramsMap));
logger.info("hiveCli task command: {}", command);
return command;
}
@Override
public AbstractParameters getParameters() {
return hiveCliParameters;
}
@Override
public void cancelApplication(boolean cancelApplication) throws Exception {
shellCommandExecutor.cancelApplication();
}
}

View File

@ -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.dolphinscheduler.plugin.task.hivecli;
import org.apache.dolphinscheduler.plugin.task.api.AbstractTask;
import org.apache.dolphinscheduler.plugin.task.api.TaskChannel;
import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext;
import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters;
import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode;
import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
public class HiveCliTaskChannel implements TaskChannel {
@Override
public void cancelApplication(boolean status) {
}
@Override
public AbstractTask createTask(TaskExecutionContext taskExecutionContext) {
return new HiveCliTask(taskExecutionContext);
}
@Override
public AbstractParameters parseParameters(ParametersNode parametersNode) {
return JSONUtils.parseObject(parametersNode.getTaskParams(), HiveCliParameters.class);
}
@Override
public ResourceParametersHelper getResources(String parameters) {
return null;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.dolphinscheduler.plugin.task.hivecli;
import org.apache.dolphinscheduler.plugin.task.api.TaskChannel;
import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import java.util.List;
import com.google.auto.service.AutoService;
@AutoService(TaskChannelFactory.class)
public class HiveCliTaskChannelFactory implements TaskChannelFactory {
@Override
public TaskChannel create() {
return new HiveCliTaskChannel();
}
@Override
public String getName() {
return "HIVECLI";
}
@Override
public List<PluginParams> getParams() {
return null;
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.dolphinscheduler.plugin.task.hivecli;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext;
import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class HiveCliTaskTest {
public static final String EXPECTED_HIVE_CLI_TASK_EXECUTE_FROM_SCRIPT_COMMAND =
"hive -e \"SHOW DATABASES;\"";
public static final String EXPECTED_HIVE_CLI_TASK_EXECUTE_FROM_FILE_COMMAND =
"hive -f sql_tasks/hive_task.sql";
public static final String EXPECTED_HIVE_CLI_TASK_EXECUTE_WITH_OPTIONS =
"hive -e \"SHOW DATABASES;\" --verbose";
@Test
public void hiveCliTaskExecuteSqlFromScript() throws Exception {
String hiveCliTaskParameters = buildHiveCliTaskExecuteSqlFromScriptParameters();
HiveCliTask hiveCliTask = prepareHiveCliTaskForTest(hiveCliTaskParameters);
hiveCliTask.init();
Assert.assertEquals(hiveCliTask.buildCommand(), EXPECTED_HIVE_CLI_TASK_EXECUTE_FROM_SCRIPT_COMMAND);
}
@Test
public void hiveCliTaskExecuteSqlFromFile() throws Exception {
String hiveCliTaskParameters = buildHiveCliTaskExecuteSqlFromFileParameters();
HiveCliTask hiveCliTask = prepareHiveCliTaskForTest(hiveCliTaskParameters);
hiveCliTask.init();
Assert.assertEquals(hiveCliTask.buildCommand(), EXPECTED_HIVE_CLI_TASK_EXECUTE_FROM_FILE_COMMAND);
}
@Test
public void hiveCliTaskExecuteWithOptions() throws Exception {
String hiveCliTaskParameters = buildHiveCliTaskExecuteWithOptionsParameters();
HiveCliTask hiveCliTask = prepareHiveCliTaskForTest(hiveCliTaskParameters);
hiveCliTask.init();
Assert.assertEquals(hiveCliTask.buildCommand(), EXPECTED_HIVE_CLI_TASK_EXECUTE_WITH_OPTIONS);
}
private HiveCliTask prepareHiveCliTaskForTest(final String hiveCliTaskParameters) {
TaskExecutionContext taskExecutionContext = Mockito.mock(TaskExecutionContext.class);
when(taskExecutionContext.getTaskParams()).thenReturn(hiveCliTaskParameters);
HiveCliTask hiveCliTask = spy(new HiveCliTask(taskExecutionContext));
return hiveCliTask;
}
private String buildHiveCliTaskExecuteSqlFromScriptParameters() {
final HiveCliParameters hiveCliParameters = new HiveCliParameters();
hiveCliParameters.setHiveCliTaskExecutionType("SCRIPT");
hiveCliParameters.setHiveSqlScript("SHOW DATABASES;");
return JSONUtils.toJsonString(hiveCliParameters);
}
private String buildHiveCliTaskExecuteSqlFromFileParameters() {
final HiveCliParameters hiveCliParameters = new HiveCliParameters();
hiveCliParameters.setHiveCliTaskExecutionType("FILE");
List<ResourceInfo> resources = new ArrayList<>();
ResourceInfo sqlResource = new ResourceInfo();
sqlResource.setResourceName("/sql_tasks/hive_task.sql");
resources.add(sqlResource);
hiveCliParameters.setResourceList(resources);
return JSONUtils.toJsonString(hiveCliParameters);
}
private String buildHiveCliTaskExecuteWithOptionsParameters() {
final HiveCliParameters hiveCliParameters = new HiveCliParameters();
hiveCliParameters.setHiveCliTaskExecutionType("SCRIPT");
hiveCliParameters.setHiveSqlScript("SHOW DATABASES;");
hiveCliParameters.setHiveCliOptions("--verbose");
return JSONUtils.toJsonString(hiveCliParameters);
}
}

View File

@ -15,15 +15,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>dolphinscheduler</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler</artifactId>
<version>dev-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dolphinscheduler-task-plugin</artifactId>
<packaging>pom</packaging>
@ -61,6 +60,7 @@
<module>dolphinscheduler-task-chunjun</module>
<module>dolphinscheduler-task-flink-stream</module>
<module>dolphinscheduler-task-pytorch</module>
<module>dolphinscheduler-task-hivecli</module>
</modules>
<dependencyManagement>
@ -74,4 +74,4 @@
</dependency>
</dependencies>
</dependencyManagement>
</project>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -341,7 +341,9 @@ export function useDataList() {
label: t('user_dropdown.password'),
key: 'password',
icon: renderIcon(KeyOutlined),
disabled: (userStore.getUserInfo as UserInfoRes).securityConfigType !== 'PASSWORD'
disabled:
(userStore.getUserInfo as UserInfoRes).securityConfigType !==
'PASSWORD'
},
{
label: t('user_dropdown.logout'),

View File

@ -646,11 +646,20 @@ export default {
zeppelin_paragraph_id_tips:
'Please enter the paragraph id of your zeppelin paragraph',
zeppelin_parameters: 'parameters',
zeppelin_parameters_tips: 'Please enter the parameters for zeppelin dynamic form',
zeppelin_parameters_tips:
'Please enter the parameters for zeppelin dynamic form',
zeppelin_rest_endpoint: 'zeppelinRestEndpoint',
zeppelin_rest_endpoint_tips: 'Please enter the rest endpoint of your Zeppelin server',
zeppelin_production_note_directory: 'Directory for cloned zeppelin note in production mode',
zeppelin_production_note_directory_tips: 'Please enter the production note directory to enable production mode',
zeppelin_rest_endpoint_tips:
'Please enter the rest endpoint of your Zeppelin server',
zeppelin_production_note_directory:
'Directory for cloned zeppelin note in production mode',
zeppelin_production_note_directory_tips:
'Please enter the production note directory to enable production mode',
hive_cli_task_execution_type: 'Hive Cli Task Execution Type',
hive_sql_script: 'Hive SQL Script',
hive_cli_options: 'Hive Cli Options',
hive_cli_options_tips:
'Please enter the options for hive cli, e.g. --verbose',
jupyter_conda_env_name: 'condaEnvName',
jupyter_conda_env_name_tips:
'Please enter the conda environment name of papermill',
@ -764,6 +773,7 @@ export default {
pytorch_python_env_tool: 'Python Environment Manager Tool',
pytorch_requirements: 'Requirement File',
pytorch_conda_python_version: 'Python Version',
pytorch_conda_python_version_tips: 'Please enter the version number, such as 3.6, 3.7, 3.x'
pytorch_conda_python_version_tips:
'Please enter the version number, such as 3.6, 3.7, 3.x'
}
}

View File

@ -638,12 +638,17 @@ export default {
zeppelin_note_id_tips: '请输入zeppelin note id',
zeppelin_paragraph_id: 'zeppelinParagraphId',
zeppelin_production_note_directory: '生产模式下存放克隆note的目录',
zeppelin_production_note_directory_tips: '请输入生产环境note目录以启用生产模式',
zeppelin_production_note_directory_tips:
'请输入生产环境note目录以启用生产模式',
zeppelin_paragraph_id_tips: '请输入zeppelin paragraph id',
zeppelin_parameters: 'parameters',
zeppelin_parameters_tips: '请输入zeppelin dynamic form参数',
zeppelin_rest_endpoint: 'zeppelinRestEndpoint',
zeppelin_rest_endpoint_tips: '请输入zeppelin server的rest endpoint',
hive_cli_task_execution_type: 'Hive Cli 任务类型',
hive_sql_script: 'Hive SQL 脚本',
hive_cli_options: 'Hive Cli 选项',
hive_cli_options_tips: '请输入您的Hive Cli选项如--verbose等',
jupyter_conda_env_name: 'condaEnvName',
jupyter_conda_env_name_tips: '请输入papermill所在的conda环境名',
jupyter_input_note_path: 'inputNotePath',

View File

@ -49,4 +49,4 @@ interface NamespaceListRes {
start: number
}
export { ListReq, K8SReq, NamespaceItem, NamespaceListRes }
export { ListReq, K8SReq, NamespaceItem, NamespaceListRes }

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, ref, toRefs } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
ref,
toRefs
} from 'vue'
import {
NSpace,
NInput,

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NSpace,
NInput,
@ -24,7 +30,7 @@ import {
NButton,
NIcon,
NDataTable,
NPagination,
NPagination
} from 'naive-ui'
import { SearchOutlined } from '@vicons/antd'
import { useTable } from './use-table'

View File

@ -30,9 +30,9 @@ import type {
ResultListRes
} from '@/service/modules/data-quality/types'
import { parseTime } from '@/common/common'
import ButtonLink from "@/components/button-link";
import { NEllipsis, NTag } from "naive-ui";
import { useRouter } from "vue-router";
import ButtonLink from '@/components/button-link'
import { NEllipsis, NTag } from 'naive-ui'
import { useRouter } from 'vue-router'
export function useTable() {
const { t } = useI18n()
@ -76,7 +76,10 @@ export function useTable() {
onClick: () =>
void router.push({
name: 'workflow-instance-detail',
params: { projectCode: row.projectCode, id: row.processInstanceId },
params: {
projectCode: row.projectCode,
id: row.processInstanceId
},
query: { code: row.processDefinitionCode }
})
},
@ -117,27 +120,27 @@ export function useTable() {
render: (row: ResultItem) => {
if (row.state === 0) {
return h(
NTag,
{ type: 'info', size: 'small' },
{
default: () => t('data_quality.task_result.undone')
}
NTag,
{ type: 'info', size: 'small' },
{
default: () => t('data_quality.task_result.undone')
}
)
} else if (row.state === 1) {
return h(
NTag,
{ type: 'success', size: 'small' },
{
default: () => t('data_quality.task_result.success')
}
NTag,
{ type: 'success', size: 'small' },
{
default: () => t('data_quality.task_result.success')
}
)
} else if (row.state === 2) {
return h(
NTag,
{ type: 'error', size: 'small' },
{
default: () => t('data_quality.task_result.failure')
}
NTag,
{ type: 'error', size: 'small' },
{
default: () => t('data_quality.task_result.failure')
}
)
} else {
return '-'

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import {
NButton,
NSpin,
@ -91,10 +97,10 @@ const DetailModal = defineComponent({
async () => {
props.show &&
state.detailForm.type &&
await changeType(
(await changeType(
state.detailForm.type,
datasourceType[state.detailForm.type]
)
))
props.show && props.id && setFieldsValue(await queryById(props.id))
}
)
@ -171,7 +177,7 @@ const DetailModal = defineComponent({
show-require-mark
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-data-source-name'
v-model={[detailForm.name, 'value']}
maxlength={60}
@ -180,7 +186,7 @@ const DetailModal = defineComponent({
</NFormItem>
<NFormItem label={t('datasource.description')} path='note'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-data-source-description'
v-model={[detailForm.note, 'value']}
type='textarea'
@ -193,7 +199,7 @@ const DetailModal = defineComponent({
show-require-mark
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-ip'
v-model={[detailForm.host, 'value']}
type='text'
@ -222,7 +228,7 @@ const DetailModal = defineComponent({
show-require-mark
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[detailForm.principal, 'value']}
type='text'
placeholder={t('datasource.principal_tips')}
@ -234,7 +240,7 @@ const DetailModal = defineComponent({
path='javaSecurityKrb5Conf'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[detailForm.javaSecurityKrb5Conf, 'value']}
type='text'
placeholder={t('datasource.krb5_conf_tips')}
@ -246,7 +252,7 @@ const DetailModal = defineComponent({
path='loginUserKeytabUsername'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[detailForm.loginUserKeytabUsername, 'value']}
type='text'
placeholder={t('datasource.keytab_username_tips')}
@ -258,7 +264,7 @@ const DetailModal = defineComponent({
path='loginUserKeytabPath'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[detailForm.loginUserKeytabPath, 'value']}
type='text'
placeholder={t('datasource.keytab_path_tips')}
@ -270,7 +276,7 @@ const DetailModal = defineComponent({
show-require-mark
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-username'
v-model={[detailForm.userName, 'value']}
type='text'
@ -283,7 +289,7 @@ const DetailModal = defineComponent({
path='password'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-password'
v-model={[detailForm.password, 'value']}
type='password'
@ -296,7 +302,7 @@ const DetailModal = defineComponent({
show-require-mark={requiredDataBase}
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-data-base'
v-model={[detailForm.database, 'value']}
type='text'
@ -326,7 +332,7 @@ const DetailModal = defineComponent({
path='other'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-jdbc-params'
v-model={[detailForm.other, 'value']}
type='textarea'

View File

@ -15,7 +15,14 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, ref, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
ref,
toRefs,
watch
} from 'vue'
import {
NButton,
NInput,
@ -113,10 +120,7 @@ const list = defineComponent({
>
{t('datasource.create_datasource')}
</NButton>
<NSpace
justify='end'
wrap={false}
>
<NSpace justify='end' wrap={false}>
<NInput
allowInput={this.trim}
v-model={[this.searchVal, 'value']}

View File

@ -51,7 +51,14 @@ const login = defineComponent({
cookies.set('language', localesStore.getLocales, { path: '/' })
return { t, handleChange, handleLogin, ...toRefs(state), localesStore, trim }
return {
t,
handleChange,
handleLogin,
...toRefs(state),
localesStore,
trim
}
},
render() {
return (

View File

@ -129,11 +129,11 @@ const master = defineComponent({
<Card title={t('monitor.master.disk_available')}>
<div class={[styles.card, styles['load-average']]}>
{item && (
<NNumberAnimation
precision={2}
from={0}
to={JSON.parse(item.resInfo).diskAvailable}
/>
<NNumberAnimation
precision={2}
from={0}
to={JSON.parse(item.resInfo).diskAvailable}
/>
)}
</div>
</Card>

View File

@ -133,11 +133,11 @@ const worker = defineComponent({
<Card title={t('monitor.worker.disk_available')}>
<div class={[styles.card, styles['load-average']]}>
{item && (
<NNumberAnimation
precision={2}
from={0}
to={JSON.parse(item.resInfo).diskAvailable}
/>
<NNumberAnimation
precision={2}
from={0}
to={JSON.parse(item.resInfo).diskAvailable}
/>
)}
</div>
</Card>

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NSpace,
NInput,
@ -85,7 +91,7 @@ const AuditLog = defineComponent({
<Card>
<NSpace justify='end'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.userName, 'value']}
size='small'
placeholder={t('monitor.audit_log.user_name')}

View File

@ -45,7 +45,7 @@ const password = defineComponent({
>
<NFormItem label={t('password.password')} path='password'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='password'
placeholder={t('password.password_tips')}
v-model={[this.passwordForm.password, 'value']}
@ -63,7 +63,7 @@ const password = defineComponent({
first
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='password'
placeholder={t('password.confirm_password_tips')}
v-model={[this.passwordForm.confirmPassword, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, ref, toRefs } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
ref,
toRefs
} from 'vue'
import { useForm } from './use-form'
import { NButton, NForm, NFormItem, NInput } from 'naive-ui'
import { useUserinfo } from './use-userinfo'
@ -93,21 +99,21 @@ const profile = defineComponent({
<NForm rules={this.rules} ref='profileFormRef'>
<NFormItem label={t('profile.username')} path='username'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.profileForm.username, 'value']}
placeholder={t('profile.username_tips')}
/>
</NFormItem>
<NFormItem label={t('profile.email')} path='email'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.profileForm.email, 'value']}
placeholder={t('profile.email_tips')}
/>
</NFormItem>
<NFormItem label={t('profile.phone')} path='phone'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.profileForm.phone, 'value']}
placeholder={t('profile.phone_tips')}
/>

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import { NForm, NFormItem, NInput } from 'naive-ui'
import { useForm } from './use-form'
import Modal from '@/components/modal'
@ -90,7 +96,7 @@ const ProjectModal = defineComponent({
}
)
return { ...toRefs(variables), t, cancelModal, confirmModal, trim}
return { ...toRefs(variables), t, cancelModal, confirmModal, trim }
},
render() {
const { t } = this
@ -112,7 +118,7 @@ const ProjectModal = defineComponent({
<NForm rules={this.rules} ref='projectFormRef'>
<NFormItem label={t('project.list.project_name')} path='projectName'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.model.projectName, 'value']}
placeholder={t('project.list.project_tips')}
class='input-project-name'
@ -120,7 +126,7 @@ const ProjectModal = defineComponent({
</NFormItem>
<NFormItem label={t('project.list.owned_users')} path='userName'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
disabled={true}
v-model={[this.model.userName, 'value']}
placeholder={t('project.list.username_tips')}
@ -131,7 +137,7 @@ const ProjectModal = defineComponent({
path='description'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.model.description, 'value']}
type='textarea'
placeholder={t('project.list.description_tips')}

View File

@ -24,7 +24,13 @@ import {
NPagination,
NSpace
} from 'naive-ui'
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import { useI18n } from 'vue-i18n'
import { useTable } from './use-table'
import Card from '@/components/card'

View File

@ -77,3 +77,4 @@ export { useSagemaker } from './use-sagemaker'
export { useChunjun } from './use-chunjun'
export { useChunjunDeployMode } from './use-chunjun-deploy-mode'
export { usePytorch } from './use-pytorch'
export { useHiveCli } from './use-hive-cli'

View File

@ -17,14 +17,18 @@
import { useI18n } from 'vue-i18n'
import { useCustomParams } from '.'
import type { IJsonItem } from '../types'
import {computed} from "vue";
import { computed } from 'vue'
export function useEmr(model: { [field: string]: any }): IJsonItem[] {
const { t } = useI18n()
const jobFlowDefineJsonSpan = computed(() => (model.programType === 'RUN_JOB_FLOW' ? 24 : 0))
const jobFlowDefineJsonSpan = computed(() =>
model.programType === 'RUN_JOB_FLOW' ? 24 : 0
)
const stepsDefineJsonSpan = computed(() => (model.programType === 'ADD_JOB_FLOW_STEPS' ? 24 : 0))
const stepsDefineJsonSpan = computed(() =>
model.programType === 'ADD_JOB_FLOW_STEPS' ? 24 : 0
)
return [
{
@ -82,4 +86,4 @@ export const PROGRAM_TYPES = [
label: 'ADD_JOB_FLOW_STEPS',
value: 'ADD_JOB_FLOW_STEPS'
}
]
]

View File

@ -0,0 +1,62 @@
/*
* 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.
*/
import { useI18n } from 'vue-i18n'
import { useCustomParams, useResources } from '.'
import type { IJsonItem } from '../types'
export function useHiveCli(model: { [field: string]: any }): IJsonItem[] {
const { t } = useI18n()
return [
{
type: 'select',
field: 'hiveCliTaskExecutionType',
span: 12,
name: t('project.node.hive_cli_task_execution_type'),
options: HIVE_CLI_TASK_EXECUTION_TYPES
},
{
type: 'editor',
field: 'hiveSqlScript',
name: t('project.node.hive_sql_script'),
props: {
language: 'sql'
}
},
{
type: 'input',
field: 'hiveCliOptions',
name: t('project.node.hive_cli_options'),
props: {
placeholder: t('project.node.hive_cli_options_tips')
}
},
useResources(),
...useCustomParams({ model, field: 'localParams', isSimple: false })
]
}
export const HIVE_CLI_TASK_EXECUTION_TYPES = [
{
label: 'FROM_SCRIPT',
value: 'SCRIPT'
},
{
label: 'FROM_FILE',
value: 'FILE'
}
]

View File

@ -47,8 +47,8 @@ export function useMlflowModels(model: { [field: string]: any }): IJsonItem[] {
watch(
() => [model.deployType],
() => {
cpuLimitSpan.value = model.deployType === "DOCKER COMPOSE" ? 12 : 0
memoryLimitSpan.value = model.deployType === "DOCKER COMPOSE" ? 12 : 0
cpuLimitSpan.value = model.deployType === 'DOCKER COMPOSE' ? 12 : 0
memoryLimitSpan.value = model.deployType === 'DOCKER COMPOSE' ? 12 : 0
}
)

View File

@ -46,7 +46,8 @@ export function usePytorch(model: { [field: string]: any }): IJsonItem[] {
isCreateEnvironmentSpan.value = model.otherParams ? 12 : 0
pythonPathSpan.value = model.otherParams ? 24 : 0
pythonEnvToolSpan.value = model.showCreateEnvironment ? 12 : 0
pythonCommandSpan.value = ~model.showCreateEnvironment & model.otherParams ? 12 : 0
pythonCommandSpan.value =
~model.showCreateEnvironment & model.otherParams ? 12 : 0
requirementsSpan.value = model.showCreateEnvironment ? 24 : 0
condaPythonVersionSpan.value = model.showCreateConda ? 24 : 0
}

View File

@ -29,7 +29,7 @@ export function useResourceLimit(): IJsonItem[] {
slots: {
suffix: () => '%'
},
props: {min: -1}
props: { min: -1 }
},
{
type: 'input-number',
@ -39,7 +39,7 @@ export function useResourceLimit(): IJsonItem[] {
slots: {
suffix: () => t('project.node.mb')
},
props: {min: -1}
props: { min: -1 }
}
]
}

View File

@ -18,19 +18,18 @@ import type { IJsonItem } from '../types'
import { useCustomParams } from '.'
export function useSagemaker(model: { [field: string]: any }): IJsonItem[] {
return [
{
type: 'editor',
field: 'sagemakerRequestJson',
name: "SagemakerRequestJson",
name: 'SagemakerRequestJson',
props: {
language: 'json'
},
validate: {
trigger: ['input', 'trigger'],
required: true,
message: 'requestJson'
message: 'requestJson'
}
},
...useCustomParams({ model, field: 'localParams', isSimple: false })

View File

@ -421,6 +421,12 @@ export function formatParams(data: INodeData): {
taskParams.targetJobName = data.targetJobName
}
if (data.taskType === 'HIVECLI') {
taskParams.hiveCliTaskExecutionType = data.hiveCliTaskExecutionType
taskParams.hiveSqlScript = data.hiveSqlScript
taskParams.hiveCliOptions = data.hiveCliOptions
}
let timeoutNotifyStrategy = ''
if (data.timeoutNotifyStrategy) {
if (data.timeoutNotifyStrategy.length === 1) {
@ -658,5 +664,6 @@ export function formatModel(data: ITaskData) {
if (data.taskParams?.jobType) {
params.isCustomTask = data.taskParams.jobType === 'CUSTOM'
}
return params
}

View File

@ -44,6 +44,7 @@ import { useDinky } from './use-dinky'
import { userSagemaker } from './use-sagemaker'
import { useChunjun } from './use-chunjun'
import { usePytorch } from './use-pytorch'
import { useHiveCli } from './use-hive-cli'
export default {
SHELL: useShell,
@ -74,5 +75,6 @@ export default {
SAGEMAKER: userSagemaker,
CHUNJUN: useChunjun,
FLINK_STREAM: useFlinkStream,
PYTORCH: usePytorch
PYTORCH: usePytorch,
HIVECLI: useHiveCli
}

View File

@ -0,0 +1,80 @@
/*
* 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.
*/
import { reactive } from 'vue'
import * as Fields from '../fields/index'
import type { IJsonItem, INodeData, ITaskData } from '../types'
export function useHiveCli({
projectCode,
from = 0,
readonly,
data
}: {
projectCode: number
from?: number
readonly?: boolean
data?: ITaskData
}) {
const model = reactive({
name: '',
taskType: 'HIVECLI',
flag: 'YES',
description: '',
timeoutFlag: false,
localParams: [],
environmentCode: null,
failRetryInterval: 1,
failRetryTimes: 0,
workerGroup: 'default',
delayTime: 0,
timeout: 30
} as INodeData)
let extra: IJsonItem[] = []
if (from === 1) {
extra = [
Fields.useTaskType(model, readonly),
Fields.useProcessName({
model,
projectCode,
isCreate: !data?.id,
from,
processName: data?.processName
})
]
}
return {
json: [
Fields.useName(from),
...extra,
Fields.useRunFlag(),
Fields.useDescription(),
Fields.useTaskPriority(),
Fields.useWorkerGroup(),
Fields.useEnvironmentName(model, !model.id),
...Fields.useTaskGroup(model, projectCode),
...Fields.useFailed(),
Fields.useDelayTime(model),
...Fields.useTimeoutAlarm(model),
...Fields.useHiveCli(model),
Fields.usePreTasks()
] as IJsonItem[],
model
}
}

View File

@ -85,4 +85,3 @@ export function usePytorch({
model
}
}

View File

@ -306,6 +306,9 @@ interface ITaskParams {
restEndpoint?: string
zeppelinProductionNoteDirectory?: string
productionNoteDirectory?: string
hiveCliOptions?: string
hiveSqlScript?: string
hiveCliTaskExecutionType?: string
noteId?: string
paragraphId?: string
condaEnvName?: string

View File

@ -44,6 +44,7 @@ export type TaskType =
| 'CHUNJUN'
| 'FLINK_STREAM'
| 'PYTORCH'
| 'HIVECLI'
export type TaskExecuteType = 'STREAM' | 'BATCH'
@ -151,6 +152,10 @@ export const TASK_TYPES_MAP = {
PYTORCH: {
alias: 'Pytorch',
helperLinkDisable: true
},
HIVECLI: {
alias: 'HIVECLI',
helperLinkDisable: true
}
} as {
[key in TaskType]: {

View File

@ -160,9 +160,7 @@ export default defineComponent({
>
<NForm ref='startFormRef' model={this.startForm}>
<NFormItem label={t('project.task.task_name')} path='task_name'>
<div title={this.row.taskName}>
{this.row.taskName}
</div>
<div title={this.row.taskName}>{this.row.taskName}</div>
</NFormItem>
<NFormItem
label={t('project.task.notification_strategy')}

View File

@ -133,8 +133,8 @@ export default defineComponent({
onClick={this.startRunning}
>
{t('project.node.start')}
</NButton>)
}
</NButton>
)}
{this.menuDisplay && (
<>
<NButton

View File

@ -159,6 +159,9 @@ $bgLight: #ffffff;
&.icon-zeppelin {
background-image: url('/images/task-icons/zeppelin.png');
}
&.icon-hivecli {
background-image: url('/images/task-icons/hivecli.png');
}
&.icon-k8s {
background-image: url('/images/task-icons/k8s.png');
}
@ -248,6 +251,9 @@ $bgLight: #ffffff;
&.icon-zeppelin {
background-image: url('/images/task-icons/zeppelin_hover.png');
}
&.icon-hivecli {
background-image: url('/images/task-icons/hivecli_hover.png');
}
&.icon-k8s {
background-image: url('/images/task-icons/k8s_hover.png');
}

View File

@ -90,7 +90,7 @@ export default defineComponent({
</NFormItem>
<NFormItem label={t('project.workflow.file_name')} path='name'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.importForm.name, 'value']}
placeholder={''}
disabled

View File

@ -27,7 +27,7 @@ import {
computed
} from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute } from "vue-router"
import { useRoute } from 'vue-router'
import Modal from '@/components/modal'
import { useForm } from './use-form'
import { useModal } from './use-modal'
@ -147,7 +147,9 @@ export default defineComponent({
}
]
const showTaskDependType = computed(() => route.name === 'workflow-definition-detail')
const showTaskDependType = computed(
() => route.name === 'workflow-definition-detail'
)
const renderLabel = (option: any) => {
return [
@ -267,13 +269,19 @@ export default defineComponent({
>
<NRadioGroup v-model:value={this.startForm.taskDependType}>
<NSpace>
<NRadio value='TASK_POST'>{t('project.workflow.backward_execution')}</NRadio>
<NRadio value='TASK_PRE'>{t('project.workflow.forward_execution')}</NRadio>
<NRadio value='TASK_ONLY'>{t('project.workflow.current_node_execution')}</NRadio>
<NRadio value='TASK_POST'>
{t('project.workflow.backward_execution')}
</NRadio>
<NRadio value='TASK_PRE'>
{t('project.workflow.forward_execution')}
</NRadio>
<NRadio value='TASK_ONLY'>
{t('project.workflow.current_node_execution')}
</NRadio>
</NSpace>
</NRadioGroup>
</NFormItem>)
}
</NFormItem>
)}
<NFormItem
label={t('project.workflow.notification_strategy')}
path='warningType'
@ -345,7 +353,9 @@ export default defineComponent({
label={t('project.workflow.mode_of_dependent')}
path='complementDependentMode'
>
<NRadioGroup v-model:value={this.startForm.complementDependentMode}>
<NRadioGroup
v-model:value={this.startForm.complementDependentMode}
>
<NSpace>
<NRadio value={'OFF_MODE'}>
{t('project.workflow.close')}
@ -380,7 +390,7 @@ export default defineComponent({
{t('project.workflow.custom_parallelism')}
</NCheckbox>
<NInput
allowInput={this.trim}
allowInput={this.trim}
disabled={!this.parallelismRef}
placeholder={t(
'project.workflow.please_enter_parallelism'
@ -419,7 +429,7 @@ export default defineComponent({
/>
) : (
<NInput
allowInput={this.trim}
allowInput={this.trim}
clearable
type='textarea'
v-model:value={this.startForm.scheduleTime}
@ -445,7 +455,7 @@ export default defineComponent({
{this.startParamsList.map((item, index) => (
<NSpace class={styles.startup} key={Date.now() + index}>
<NInput
allowInput={this.trim}
allowInput={this.trim}
pair
separator=':'
placeholder={['prop', 'value']}

View File

@ -266,7 +266,7 @@ export default defineComponent({
{{
trigger: () => (
<NInput
allowInput={this.trim}
allowInput={this.trim}
style={{ width: '80%' }}
readonly={true}
v-model:value={this.timingForm.crontab}

View File

@ -26,7 +26,13 @@ import {
NTooltip,
NPopconfirm
} from 'naive-ui'
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import { useI18n } from 'vue-i18n'
import { useTable } from './use-table'
import { useRouter, useRoute } from 'vue-router'
@ -128,7 +134,12 @@ export default defineComponent({
>
{t('project.workflow.create_workflow')}
</NButton>
<NButton strong secondary size='small' onClick={() => (this.showRef = true)}>
<NButton
strong
secondary
size='small'
onClick={() => (this.showRef = true)}
>
{t('project.workflow.import_workflow')}
</NButton>
</NSpace>

View File

@ -16,14 +16,7 @@
*/
import { SearchOutlined } from '@vicons/antd'
import {
NInput,
NButton,
NDatePicker,
NSelect,
NIcon,
NSpace
} from 'naive-ui'
import { NInput, NButton, NDatePicker, NSelect, NIcon, NSpace } from 'naive-ui'
import { defineComponent, getCurrentInstance, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { format } from 'date-fns'
@ -102,7 +95,7 @@ export default defineComponent({
<NSelect
options={options}
size='small'
style={{width: '210px'}}
style={{ width: '210px' }}
defaultValue={''}
v-model:value={this.stateTypeRef}
/>

View File

@ -75,7 +75,7 @@ export default defineComponent({
>
<NFormItem label={t('resource.file.file_name')} path='fileName'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.fileForm.fileName, 'value']}
placeholder={t('resource.file.enter_name_tips')}
style={{ width: '300px' }}
@ -93,7 +93,7 @@ export default defineComponent({
</NFormItem>
<NFormItem label={t('resource.file.description')} path='description'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='textarea'
v-model={[this.fileForm.description, 'value']}
placeholder={t('resource.file.enter_description_tips')}

View File

@ -16,7 +16,14 @@
*/
import { useRouter } from 'vue-router'
import { defineComponent, onMounted, ref, reactive, Ref, getCurrentInstance } from 'vue'
import {
defineComponent,
onMounted,
ref,
reactive,
Ref,
getCurrentInstance
} from 'vue'
import {
NIcon,
NSpace,

View File

@ -14,7 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineComponent, toRefs, PropType, watch, getCurrentInstance } from 'vue'
import {
defineComponent,
toRefs,
PropType,
watch,
getCurrentInstance
} from 'vue'
import { NForm, NFormItem, NInput } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import Modal from '@/components/modal'
@ -84,7 +90,7 @@ export default defineComponent({
<NForm rules={this.rules} ref='renameFormRef'>
<NFormItem label={t('resource.file.name')} path='name'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.renameForm.name, 'value']}
placeholder={t('resource.file.enter_name_tips')}
class='input-name'
@ -92,7 +98,7 @@ export default defineComponent({
</NFormItem>
<NFormItem label={t('resource.file.description')} path='description'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='textarea'
v-model={[this.renameForm.description, 'value']}
placeholder={t('resource.file.enter_description_tips')}

View File

@ -87,7 +87,7 @@ export default defineComponent({
ref='uploadFormNameRef'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.uploadForm.name, 'value']}
placeholder={t('resource.file.enter_name_tips')}
class='input-file-name'
@ -95,7 +95,7 @@ export default defineComponent({
</NFormItem>
<NFormItem label={t('resource.file.description')} path='description'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='textarea'
v-model={[this.uploadForm.description, 'value']}
placeholder={t('resource.file.enter_description_tips')}

View File

@ -15,7 +15,14 @@
* limitations under the License.
*/
import { ref, defineComponent, toRefs, reactive, onMounted, getCurrentInstance } from 'vue'
import {
ref,
defineComponent,
toRefs,
reactive,
onMounted,
getCurrentInstance
} from 'vue'
import {
NButton,
NIcon,

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, PropType, toRefs, onMounted, getCurrentInstance } from 'vue'
import {
defineComponent,
PropType,
toRefs,
onMounted,
getCurrentInstance
} from 'vue'
import { NForm, NFormItem, NInput } from 'naive-ui'
import { useForm } from '../use-form'
import Modal from '@/components/modal'
@ -83,7 +89,9 @@ const FormModal = defineComponent({
path='priority'
>
<NInput
allowInput={this.trim} v-model:value={this.formData.priority} />
allowInput={this.trim}
v-model:value={this.formData.priority}
/>
</NFormItem>
</NForm>
</Modal>

View File

@ -15,7 +15,14 @@
* limitations under the License.
*/
import { ref, defineComponent, toRefs, reactive, onMounted, getCurrentInstance } from 'vue'
import {
ref,
defineComponent,
toRefs,
reactive,
onMounted,
getCurrentInstance
} from 'vue'
import {
NButton,
NIcon,

View File

@ -15,7 +15,15 @@
* limitations under the License.
*/
import { defineComponent, toRefs, PropType, watch, onMounted, ref, getCurrentInstance } from 'vue'
import {
defineComponent,
toRefs,
PropType,
watch,
onMounted,
ref,
getCurrentInstance
} from 'vue'
import {
NUpload,
NIcon,
@ -146,7 +154,7 @@ export default defineComponent({
path='funcName'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.functionForm.funcName, 'value']}
placeholder={t('resource.function.enter_udf_unction_name_tips')}
class='input-function-name'
@ -157,7 +165,7 @@ export default defineComponent({
path='className'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.functionForm.className, 'value']}
placeholder={t('resource.function.enter_package_name_tips')}
class='input-class-name'
@ -217,7 +225,7 @@ export default defineComponent({
>
<NInputGroup>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.uploadForm.name, 'value']}
placeholder={t('resource.function.enter_name_tips')}
/>
@ -263,7 +271,7 @@ export default defineComponent({
path='description'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='textarea'
v-model={[this.functionForm.description, 'value']}
placeholder={t('resource.function.enter_instructions_tips')}

View File

@ -15,7 +15,15 @@
* limitations under the License.
*/
import { defineComponent, Ref, toRefs, onMounted, toRef, watch, getCurrentInstance } from 'vue'
import {
defineComponent,
Ref,
toRefs,
onMounted,
toRef,
watch,
getCurrentInstance
} from 'vue'
import {
NIcon,
NSpace,
@ -114,7 +122,7 @@ export default defineComponent({
/>
<NButton type='primary' size='small' onClick={this.handleSearch}>
<NIcon>
<SearchOutlined/>
<SearchOutlined />
</NIcon>
</NButton>
</NSpace>

View File

@ -86,7 +86,7 @@ export default defineComponent({
<NForm rules={this.rules} ref='uploadFormRef'>
<NFormItem label={t('resource.udf.file_name')} path='name'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.uploadForm.name, 'value']}
placeholder={t('resource.udf.enter_name_tips')}
class='input-file-name'
@ -94,7 +94,7 @@ export default defineComponent({
</NFormItem>
<NFormItem label={t('resource.udf.description')} path='description'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='textarea'
v-model={[this.uploadForm.description, 'value']}
placeholder={t('resource.udf.enter_description_tips')}

View File

@ -15,7 +15,15 @@
* limitations under the License.
*/
import { defineComponent, Ref, toRefs, onMounted, toRef, watch, getCurrentInstance } from 'vue'
import {
defineComponent,
Ref,
toRefs,
onMounted,
toRef,
watch,
getCurrentInstance
} from 'vue'
import {
NIcon,
NSpace,

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput, NSelect } from 'naive-ui'
import { useModal } from './use-modal'
@ -126,7 +132,7 @@ const AlarmGroupModal = defineComponent({
path='groupName'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
placeholder={t(
'security.alarm_group.alert_group_name_tips'
)}
@ -151,7 +157,7 @@ const AlarmGroupModal = defineComponent({
path='description'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
type='textarea'
placeholder={t(
'security.alarm_group.alarm_group_description_tips'

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NButton,
NDataTable,
@ -112,7 +118,7 @@ const alarmGroupManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
clearable
v-model={[this.searchVal, 'value']}

View File

@ -15,7 +15,14 @@
* limitations under the License.
*/
import { defineComponent, toRefs, watch, onMounted, ref, getCurrentInstance } from 'vue'
import {
defineComponent,
toRefs,
watch,
onMounted,
ref,
getCurrentInstance
} from 'vue'
import { NSelect, NInput } from 'naive-ui'
import { isFunction } from 'lodash'
import { useI18n } from 'vue-i18n'
@ -168,7 +175,7 @@ const DetailModal = defineComponent({
label: t('security.alarm_instance.alarm_instance_name'),
widget: (
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[detailForm.instanceName, 'value']}
placeholder={t(
'security.alarm_instance.alarm_instance_name_tips'

View File

@ -15,7 +15,14 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, ref, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
ref,
toRefs,
watch
} from 'vue'
import {
NButton,
NInput,
@ -121,12 +128,9 @@ const AlarmInstanceManage = defineComponent({
{t('security.alarm_instance.create_alarm_instance')}
</NButton>
)}
<NSpace
justify='end'
wrap={false}
>
<NSpace justify='end' wrap={false}>
<NInput
allowInput={this.trim}
allowInput={this.trim}
v-model={[this.searchVal, 'value']}
size='small'
placeholder={`${t(
@ -145,7 +149,12 @@ const AlarmInstanceManage = defineComponent({
</Card>
<Card title={t('menu.alarm_instance_manage')}>
<NSpace vertical>
<NDataTable columns={columns} data={list} loading={loading} striped />
<NDataTable
columns={columns}
data={list}
loading={loading}
striped
/>
<NSpace justify='center'>
<NPagination
page={page}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput } from 'naive-ui'
import { useModal } from './use-modal'
@ -143,7 +149,7 @@ const ClusterModal = defineComponent({
path='name'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-cluster-name'
placeholder={t('security.cluster.cluster_name_tips')}
v-model={[this.model.name, 'value']}
@ -154,7 +160,7 @@ const ClusterModal = defineComponent({
path='k8s_config'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-cluster-config'
placeholder={envK8sConfigPlaceholder}
type='textarea'
@ -167,7 +173,7 @@ const ClusterModal = defineComponent({
path='yarn_config'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-yarn-config'
placeholder={envYarnConfigPlaceholder}
disabled={true}
@ -179,7 +185,7 @@ const ClusterModal = defineComponent({
path='description'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-cluster-desc'
placeholder={t('security.cluster.cluster_description_tips')}
v-model={[this.model.description, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NButton,
NDataTable,
@ -117,7 +123,7 @@ const clusterManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
clearable
v-model={[this.searchVal, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput, NSelect } from 'naive-ui'
import { useModal } from './use-modal'
@ -141,7 +147,7 @@ const EnvironmentModal = defineComponent({
path='name'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-environment-name'
placeholder={t(
'security.environment.environment_name_tips'
@ -154,7 +160,7 @@ const EnvironmentModal = defineComponent({
path='config'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-environment-config'
placeholder={envConfigPlaceholder}
type='textarea'
@ -167,7 +173,7 @@ const EnvironmentModal = defineComponent({
path='description'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-environment-desc'
placeholder={t(
'security.environment.environment_description_tips'

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NButton,
NDataTable,
@ -117,7 +123,7 @@ const environmentManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
clearable
v-model={[this.searchVal, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import {
NForm,
@ -67,35 +73,22 @@ const K8sNamespaceModal = defineComponent({
const trim = getCurrentInstance()?.appContext.config.globalProperties.trim
watch(
() => props.showModalRef,
() => {
props.showModalRef && getListData()
}
() => props.showModalRef,
() => {
props.showModalRef && getListData()
}
)
watch(
() => props.statusRef,
() => {
if (props.statusRef === 0) {
variables.model.namespace = ''
variables.model.clusterCode = ''
variables.model.limitsCpu = ''
variables.model.limitsMemory = ''
variables.model.userId = ''
} else {
variables.model.id = props.row.id
variables.model.namespace = props.row.namespace
variables.model.clusterCode = props.row.clusterCode
variables.model.limitsCpu = props.row.limitsCpu + ''
variables.model.limitsMemory = props.row.limitsMemory + ''
variables.model.userId = props.row.userId
}
}
)
watch(
() => props.row,
() => {
() => props.statusRef,
() => {
if (props.statusRef === 0) {
variables.model.namespace = ''
variables.model.clusterCode = ''
variables.model.limitsCpu = ''
variables.model.limitsMemory = ''
variables.model.userId = ''
} else {
variables.model.id = props.row.id
variables.model.namespace = props.row.namespace
variables.model.clusterCode = props.row.clusterCode
@ -103,6 +96,19 @@ const K8sNamespaceModal = defineComponent({
variables.model.limitsMemory = props.row.limitsMemory + ''
variables.model.userId = props.row.userId
}
}
)
watch(
() => props.row,
() => {
variables.model.id = props.row.id
variables.model.namespace = props.row.namespace
variables.model.clusterCode = props.row.clusterCode
variables.model.limitsCpu = props.row.limitsCpu + ''
variables.model.limitsMemory = props.row.limitsMemory + ''
variables.model.userId = props.row.userId
}
)
return { t, ...toRefs(variables), cancelModal, confirmModal, trim }
@ -110,83 +116,87 @@ const K8sNamespaceModal = defineComponent({
render() {
const { t } = this
return (
<div>
<Modal
title={
this.statusRef === 0
? t('security.k8s_namespace.create_namespace')
: t('security.k8s_namespace.edit_namespace')
}
show={this.showModalRef}
onCancel={this.cancelModal}
onConfirm={this.confirmModal}
confirmDisabled={!this.model.namespace || (this.model.clusterCode == null || this.model.clusterCode === '')}
confirmLoading={this.saving}
>
{{
default: () => (
<NForm
model={this.model}
rules={this.rules}
ref='k8sNamespaceFormRef'
>
<NFormItem
label={t('security.k8s_namespace.k8s_namespace')}
path='namespace'
>
<NInput
allowInput={this.trim}
placeholder={t('security.k8s_namespace.k8s_namespace_tips')}
v-model={[this.model.namespace, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.k8s_cluster')}
path='clusterCode'
>
<NSelect
placeholder={t('security.k8s_namespace.k8s_cluster_tips')}
options={this.model.clusterOptions}
v-model={[this.model.clusterCode, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_cpu')}
path='limitsCpu'
>
<NInputGroup>
<NInput
allowInput={this.trim}
placeholder={t('security.k8s_namespace.limit_cpu_tips')}
v-model={[this.model.limitsCpu, 'value']}
/>
<NInputGroupLabel>CORE</NInputGroupLabel>
</NInputGroup>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_memory')}
path='limitsMemory'
>
<NInputGroup>
<NInput
allowInput={this.trim}
placeholder={t(
'security.k8s_namespace.limit_memory_tips'
)}
v-model={[this.model.limitsMemory, 'value']}
/>
<NInputGroupLabel>GB</NInputGroupLabel>
</NInputGroup>
</NFormItem>
</NForm>
)
}}
</Modal>
</div>
<div>
<Modal
title={
this.statusRef === 0
? t('security.k8s_namespace.create_namespace')
: t('security.k8s_namespace.edit_namespace')
}
show={this.showModalRef}
onCancel={this.cancelModal}
onConfirm={this.confirmModal}
confirmDisabled={
!this.model.namespace ||
this.model.clusterCode == null ||
this.model.clusterCode === ''
}
confirmLoading={this.saving}
>
{{
default: () => (
<NForm
model={this.model}
rules={this.rules}
ref='k8sNamespaceFormRef'
>
<NFormItem
label={t('security.k8s_namespace.k8s_namespace')}
path='namespace'
>
<NInput
allowInput={this.trim}
placeholder={t('security.k8s_namespace.k8s_namespace_tips')}
v-model={[this.model.namespace, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.k8s_cluster')}
path='clusterCode'
>
<NSelect
placeholder={t('security.k8s_namespace.k8s_cluster_tips')}
options={this.model.clusterOptions}
v-model={[this.model.clusterCode, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_cpu')}
path='limitsCpu'
>
<NInputGroup>
<NInput
allowInput={this.trim}
placeholder={t('security.k8s_namespace.limit_cpu_tips')}
v-model={[this.model.limitsCpu, 'value']}
/>
<NInputGroupLabel>CORE</NInputGroupLabel>
</NInputGroup>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_memory')}
path='limitsMemory'
>
<NInputGroup>
<NInput
allowInput={this.trim}
placeholder={t(
'security.k8s_namespace.limit_memory_tips'
)}
v-model={[this.model.limitsMemory, 'value']}
/>
<NInputGroupLabel>GB</NInputGroupLabel>
</NInputGroup>
</NFormItem>
</NForm>
)
}}
</Modal>
</div>
)
}
})
export default K8sNamespaceModal
export default K8sNamespaceModal

View File

@ -26,8 +26,8 @@ import { queryAllClusterList } from '@/service/modules/cluster'
import { useAsyncState } from '@vueuse/core'
export function useModal(
props: any,
ctx: SetupContext<('cancelModal' | 'confirmModal')[]>
props: any,
ctx: SetupContext<('cancelModal' | 'confirmModal')[]>
) {
const { t } = useI18n()
@ -73,8 +73,8 @@ export function useModal(
try {
statusRef === 0
? await submitK8SNamespaceModal()
: await updateK8SNamespaceModal()
? await submitK8SNamespaceModal()
: await updateK8SNamespaceModal()
variables.saving = false
} catch (err) {
variables.saving = false
@ -83,23 +83,23 @@ export function useModal(
const getListData = () => {
const { state } = useAsyncState(
queryAllClusterList().then((res: any) => {
variables.model.clusterOptions = res
.filter((item: any) => {
if (item.config) {
const k8s = JSON.parse(item.config).k8s
return !!k8s
}
return false
})
.map((item: any) => {
return {
label: item.name,
value: item.code
}
})
}),
{}
queryAllClusterList().then((res: any) => {
variables.model.clusterOptions = res
.filter((item: any) => {
if (item.config) {
const k8s = JSON.parse(item.config).k8s
return !!k8s
}
return false
})
.map((item: any) => {
return {
label: item.name,
value: item.code
}
})
}),
{}
)
return state
@ -120,9 +120,9 @@ export function useModal(
const updateK8SNamespaceModal = () => {
updateK8sNamespace(variables.model, variables.model.id).then(
(ignored: any) => {
ctx.emit('confirmModal', props.showModalRef)
}
(ignored: any) => {
ctx.emit('confirmModal', props.showModalRef)
}
)
}
@ -131,4 +131,4 @@ export function useModal(
handleValidate,
getListData
}
}
}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NButton,
NDataTable,
@ -112,7 +118,7 @@ const k8sNamespaceManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
clearable
v-model={[this.searchVal, 'value']}

View File

@ -50,9 +50,9 @@ export function useTable() {
getTableData({
pageSize: variables.pageSize,
pageNo:
variables.tableData.length === 1 && variables.page > 1
? variables.page - 1
: variables.page,
variables.tableData.length === 1 && variables.page > 1
? variables.page - 1
: variables.page,
searchVal: variables.searchVal
})
})
@ -104,61 +104,61 @@ export function useTable() {
return h(NSpace, null, {
default: () => [
h(
NTooltip,
{},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'info',
size: 'small',
onClick: () => {
handleEdit(row)
}
},
{
icon: () =>
h(NIcon, null, { default: () => h(EditOutlined) })
}
),
default: () => t('security.k8s_namespace.edit')
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDelete(row)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'info',
size: 'small',
onClick: () => {
handleEdit(row)
}
type: 'error',
size: 'small'
},
{
icon: () =>
h(NIcon, null, { default: () => h(EditOutlined) })
h(NIcon, null, {
default: () => h(DeleteOutlined)
})
}
),
default: () => t('security.k8s_namespace.edit')
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDelete(row)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'small'
},
{
icon: () =>
h(NIcon, null, {
default: () => h(DeleteOutlined)
})
}
),
default: () => t('security.k8s_namespace.delete')
}
),
default: () => t('security.k8s_namespace.delete_confirm')
}
),
default: () => t('security.k8s_namespace.delete')
}
),
default: () => t('security.k8s_namespace.delete_confirm')
}
)
]
})
@ -188,24 +188,24 @@ export function useTable() {
if (variables.loadingRef) return
variables.loadingRef = true
const { state } = useAsyncState(
queryNamespaceListPaging({ ...params }).then((res: NamespaceListRes) => {
variables.tableData = res.totalList.map((item, unused) => {
item.createTime = format(
parseTime(item.createTime),
'yyyy-MM-dd HH:mm:ss'
)
item.updateTime = format(
parseTime(item.updateTime),
'yyyy-MM-dd HH:mm:ss'
)
return {
...item
}
}) as any
variables.totalPage = res.totalPage
variables.loadingRef = false
}),
{}
queryNamespaceListPaging({ ...params }).then((res: NamespaceListRes) => {
variables.tableData = res.totalList.map((item, unused) => {
item.createTime = format(
parseTime(item.createTime),
'yyyy-MM-dd HH:mm:ss'
)
item.updateTime = format(
parseTime(item.updateTime),
'yyyy-MM-dd HH:mm:ss'
)
return {
...item
}
}) as any
variables.totalPage = res.totalPage
variables.loadingRef = false
}),
{}
)
return state
@ -216,4 +216,4 @@ export function useTable() {
getTableData,
createColumns
}
}
}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput, NSelect } from 'naive-ui'
import { useModalData } from './use-modalData'
@ -123,7 +129,7 @@ const TenantModal = defineComponent({
path='tenantCode'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-tenant-code'
disabled={this.statusRef === 1}
placeholder={t('security.tenant.tenant_code_tips')}
@ -146,7 +152,7 @@ const TenantModal = defineComponent({
path='description'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-description'
placeholder={t('security.tenant.description_tips')}
v-model={[this.model.description, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, toRefs, onMounted, watch, getCurrentInstance } from 'vue'
import {
defineComponent,
toRefs,
onMounted,
watch,
getCurrentInstance
} from 'vue'
import {
NButton,
NInput,
@ -107,7 +113,7 @@ const tenementManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
v-model={[this.searchVal, 'value']}
placeholder={t('security.tenant.search_tips')}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import { useI18n } from 'vue-i18n'
import {
NInput,
@ -108,7 +114,7 @@ export const UserModal = defineComponent({
>
<NFormItem label={t('security.user.username')} path='userName'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-username'
v-model:value={this.formData.userName}
minlength={3}
@ -122,7 +128,7 @@ export const UserModal = defineComponent({
path='userPassword'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-password'
type='password'
v-model:value={this.formData.userPassword}
@ -151,7 +157,7 @@ export const UserModal = defineComponent({
)}
<NFormItem label={t('security.user.email')} path='email'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-email'
v-model:value={this.formData.email}
placeholder={t('security.user.email_empty_tips')}
@ -159,7 +165,7 @@ export const UserModal = defineComponent({
</NFormItem>
<NFormItem label={t('security.user.phone')} path='phone'>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-phone'
v-model:value={this.formData.phone}
placeholder={t('security.user.phone_empty_tips')}

View File

@ -80,7 +80,11 @@ const UsersManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim} v-model:value={this.searchVal} size='small' clearable />
allowInput={this.trim}
v-model:value={this.searchVal}
size='small'
clearable
/>
<NButton type='primary' size='small' onClick={this.onUpdatedList}>
<NIcon>
<SearchOutlined />

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput, NSelect } from 'naive-ui'
import { useModal } from './use-modal'
@ -118,7 +124,7 @@ const WorkerGroupModal = defineComponent({
path='name'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-worker-group-name'
placeholder={t('security.worker_group.group_name_tips')}
v-model={[this.model.name, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NButton,
NDataTable,
@ -117,7 +123,7 @@ const workerGroupManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
clearable
v-model={[this.searchVal, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, PropType, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
PropType,
toRefs,
watch
} from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput } from 'naive-ui'
import { useModal } from './use-modal'
@ -111,7 +117,7 @@ const YarnQueueModal = defineComponent({
path='queueName'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-queue-name'
placeholder={t('security.yarn_queue.queue_name_tips')}
v-model={[this.model.queueName, 'value']}
@ -122,7 +128,7 @@ const YarnQueueModal = defineComponent({
path='queue'
>
<NInput
allowInput={this.trim}
allowInput={this.trim}
class='input-queue-value'
placeholder={t('security.yarn_queue.queue_value_tips')}
v-model={[this.model.queue, 'value']}

View File

@ -15,7 +15,13 @@
* limitations under the License.
*/
import { defineComponent, getCurrentInstance, onMounted, toRefs, watch } from 'vue'
import {
defineComponent,
getCurrentInstance,
onMounted,
toRefs,
watch
} from 'vue'
import {
NButton,
NDataTable,
@ -117,7 +123,7 @@ const yarnQueueManage = defineComponent({
</NButton>
<NSpace>
<NInput
allowInput={this.trim}
allowInput={this.trim}
size='small'
clearable
v-model={[this.searchVal, 'value']}