diff --git a/docs/docs/en/guide/datasource/redshift.md b/docs/docs/en/guide/datasource/redshift.md index 60dd982492..712815a3e0 100644 --- a/docs/docs/en/guide/datasource/redshift.md +++ b/docs/docs/en/guide/datasource/redshift.md @@ -1,7 +1,5 @@ # Amazon Redshift -![Redshift datasource](../../../../img/new_ui/dev/datasource/redshift.png) - ## Datasource Parameters | **Datasource** | **Description** | @@ -11,11 +9,29 @@ | Description | Enter a description of the datasource. | | IP/Host Name | Enter the Redshift service IP. | | Port | Enter the Redshift service port. | +| Validation | Enter the Redshift authentication mode. | | Username | Set the username for Redshift connection. | | Password | Set the password for Redshift connection. | | Database Name | Enter the database name of the Redshift connection. | | jdbc connect parameters | Parameter settings for Redshift connection, in JSON format. | +| AccessKeyID | Mode IAM-accessKey access key ID. | +| SecretAccessKey | Mode IAM-accessKey secret access key. | + +### Validation: Password + +![password](../../../../img/new_ui/dev/datasource/redshift-password.png) + +Use AWS redshift database username and password to login. + +### Validation: IAM-accessKey + +![IAM1](../../../../img/new_ui/dev/datasource/redshift-iam1.png) +![IAM2](../../../../img/new_ui/dev/datasource/redshift-iam2.png) + +Use cluster ID, AWS Region, port(optional) and IAM to login. ## Native Supported Yes, could use this datasource by default. + +Read more about Redshift IAM JDBC driver configuration reference document [redshift-connect-IAM-jdbc](https://docs.aws.amazon.com/redshift/latest/mgmt/generating-iam-credentials-configure-jdbc-odbc.html) diff --git a/docs/docs/zh/guide/datasource/redshift.md b/docs/docs/zh/guide/datasource/redshift.md new file mode 100644 index 0000000000..f08753c971 --- /dev/null +++ b/docs/docs/zh/guide/datasource/redshift.md @@ -0,0 +1,36 @@ +# Amazon Redshift + +## 数据源参数 + +使用数据库服务器的用户名和密码验证。 +- 数据源:选择 AZURE Redshift +- 数据源名称:输入数据源的名称 +- 描述:输入数据源的描述 +- IP 主机名:输入连接 Redshift 的 HOST 或 IP ,例如:cluster-name.xxx.region.redshift.amazonaws.com.cn +- 端口:输入连接 Redshift 的端口,默认5439 +- 验证模式:输入 Redshift 的连接模式,目前支持:Password,IAM-accessKey +- 用户名:设置连接 Redshift 的用户名 +- 密码:设置连接 Redshift 的密码 +- 数据库名:输入连接 Redshift 的数据库名称 +- Jdbc 连接参数:用于 Redshift 连接的参数设置,以 JSON 形式填写 +- AccessKeyID:IAM-accessKey模式下的access key ID +- SecretAccessKey:IAM-accessKey模式下的secret access key + +### 验证: Password + +![password](../../../../img/new_ui/dev/datasource/redshift-password.png) + +使用Redshift数据库的用户名和密码验证。 + +### 验证: IAM-accessKey + +![IAM1](../../../../img/new_ui/dev/datasource/redshift-iam1.png) +![IAM2](../../../../img/new_ui/dev/datasource/redshift-iam2.png) + +使用 cluster ID, AWS Region, port(可选) and IAM信息来登录。 + +## 是否原生支持 + +是,数据源不需要任务附加操作即可使用。 + +参考更多关于Redshift相关的JDBC文档[校验模式](https://docs.aws.amazon.com/redshift/latest/mgmt/generating-iam-credentials-configure-jdbc-odbc.html) diff --git a/docs/img/new_ui/dev/datasource/redshift-iam1.png b/docs/img/new_ui/dev/datasource/redshift-iam1.png new file mode 100644 index 0000000000..f2355c63ca Binary files /dev/null and b/docs/img/new_ui/dev/datasource/redshift-iam1.png differ diff --git a/docs/img/new_ui/dev/datasource/redshift-iam2.png b/docs/img/new_ui/dev/datasource/redshift-iam2.png new file mode 100644 index 0000000000..7f5a021dc9 Binary files /dev/null and b/docs/img/new_ui/dev/datasource/redshift-iam2.png differ diff --git a/docs/img/new_ui/dev/datasource/redshift-password.png b/docs/img/new_ui/dev/datasource/redshift-password.png new file mode 100644 index 0000000000..f56c8fbfd4 Binary files /dev/null and b/docs/img/new_ui/dev/datasource/redshift-password.png differ diff --git a/docs/img/new_ui/dev/datasource/redshift.png b/docs/img/new_ui/dev/datasource/redshift.png deleted file mode 100644 index 333fe0214a..0000000000 Binary files a/docs/img/new_ui/dev/datasource/redshift.png and /dev/null differ diff --git a/dolphinscheduler-bom/pom.xml b/dolphinscheduler-bom/pom.xml index 6d2c156b16..a691d3f2cc 100644 --- a/dolphinscheduler-bom/pom.xml +++ b/dolphinscheduler-bom/pom.xml @@ -101,6 +101,8 @@ 1.15 402 1.7.1 + 2.1.0.9 + 1.12.300 @@ -796,6 +798,18 @@ azure-identity ${azure-identity.version} + + com.amazon.redshift + redshift-jdbc42 + ${redshift-jdbc42.version} + test + + + com.amazonaws + aws-java-sdk-redshift + ${aws-java-sdk-redshift.version} + test + diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java index 7400636a9d..58602ba3b2 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java @@ -67,6 +67,7 @@ public class DataSourceConstants { public static final String JDBC_DB2 = "jdbc:db2://"; public static final String JDBC_PRESTO = "jdbc:presto://"; public static final String JDBC_REDSHIFT = "jdbc:redshift://"; + public static final String JDBC_REDSHIFT_IAM = "jdbc:redshift:iam://"; public static final String JDBC_ATHENA = "jdbc:awsathena://"; public static final String JDBC_TRINO = "jdbc:trino://"; public static final String JDBC_DAMENG = "jdbc:dm://"; diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/datasource/AbstractDataSourceProcessor.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/datasource/AbstractDataSourceProcessor.java index ae0bad6b59..a5d7502762 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/datasource/AbstractDataSourceProcessor.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/datasource/AbstractDataSourceProcessor.java @@ -49,7 +49,10 @@ public abstract class AbstractDataSourceProcessor implements DataSourceProcessor @Override public void checkDatasourceParam(BaseDataSourceParamDTO baseDataSourceParamDTO) { - checkHost(baseDataSourceParamDTO.getHost()); + if (!baseDataSourceParamDTO.getType().equals(DbType.REDSHIFT)) { + // due to redshift use not regular hosts + checkHost(baseDataSourceParamDTO.getHost()); + } checkDatabasePatter(baseDataSourceParamDTO.getDatabase()); checkOther(baseDataSourceParamDTO.getOther()); } diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/pom.xml b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/pom.xml index 041dbea12e..850aef34d1 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/pom.xml +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/pom.xml @@ -38,6 +38,14 @@ org.apache.dolphinscheduler dolphinscheduler-datasource-api + + com.amazon.redshift + redshift-jdbc42 + + + com.amazonaws + aws-java-sdk-redshift + diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/RedshiftDataSourceClient.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/RedshiftDataSourceClient.java index c9a31080d5..944d507d6d 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/RedshiftDataSourceClient.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/RedshiftDataSourceClient.java @@ -18,12 +18,65 @@ package org.apache.dolphinscheduler.plugin.datasource.redshift; import org.apache.dolphinscheduler.plugin.datasource.api.client.CommonDataSourceClient; +import org.apache.dolphinscheduler.plugin.datasource.redshift.param.RedshiftAuthMode; +import org.apache.dolphinscheduler.plugin.datasource.redshift.param.RedshiftConnectionParam; +import org.apache.dolphinscheduler.plugin.datasource.redshift.param.RedshiftDataSourceProcessor; import org.apache.dolphinscheduler.spi.datasource.BaseConnectionParam; import org.apache.dolphinscheduler.spi.enums.DbType; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Stopwatch; + public class RedshiftDataSourceClient extends CommonDataSourceClient { + private static final Logger logger = LoggerFactory.getLogger(RedshiftDataSourceClient.class); + public RedshiftDataSourceClient(BaseConnectionParam baseConnectionParam, DbType dbType) { super(baseConnectionParam, dbType); } + + @Override + public Connection getConnection() { + RedshiftConnectionParam connectionParam = (RedshiftConnectionParam) this.baseConnectionParam; + if (connectionParam.getMode().equals(RedshiftAuthMode.PASSWORD)) { + return super.getConnection(); + } + return RedshiftDataSourceProcessor.getConnectionByIAM(connectionParam); + } + + @Override + public void checkClient() { + RedshiftConnectionParam connectionParam = (RedshiftConnectionParam) this.baseConnectionParam; + Stopwatch stopwatch = Stopwatch.createStarted(); + String validationQuery = this.baseConnectionParam.getValidationQuery(); + if (connectionParam.getMode().equals(RedshiftAuthMode.PASSWORD)) { + // Checking data source client + try { + this.jdbcTemplate.execute(validationQuery); + } catch (Exception e) { + throw new RuntimeException("JDBC connect failed", e); + } finally { + logger.info("Time to execute check jdbc client with sql {} for {} ms ", + this.baseConnectionParam.getValidationQuery(), stopwatch.elapsed(TimeUnit.MILLISECONDS)); + } + } else { + try (Statement statement = getConnection().createStatement()) { + if (!statement.execute(validationQuery)) { + throw new SQLException("execute check redshift access key failed : " + validationQuery); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + logger.info("Time to execute check redshift access key with sql {} for {} ms ", + this.baseConnectionParam.getValidationQuery(), stopwatch.elapsed(TimeUnit.MILLISECONDS)); + } + } + } } diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftAuthMode.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftAuthMode.java new file mode 100644 index 0000000000..2fa62ae2eb --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftAuthMode.java @@ -0,0 +1,65 @@ +/* + * 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.datasource.redshift.param; + +import static java.util.stream.Collectors.toMap; + +import java.util.Arrays; +import java.util.Map; +import java.util.NoSuchElementException; + +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.base.Functions; + +public enum RedshiftAuthMode { + + PASSWORD(0, "password"), + IAM_ACCESS_KEY(1, "IAM-accessKey"), + ; + + private static final Map AUTH_TYPE_MAP = + Arrays.stream(RedshiftAuthMode.values()).collect(toMap(RedshiftAuthMode::getCode, Functions.identity())); + private final int code; + @JsonValue + private final String descp; + + RedshiftAuthMode(int code, String descp) { + this.code = code; + this.descp = descp; + } + + public static RedshiftAuthMode of(int type) { + if (AUTH_TYPE_MAP.containsKey(type)) { + return AUTH_TYPE_MAP.get(type); + } + return null; + } + + public static RedshiftAuthMode ofName(String name) { + return Arrays.stream(RedshiftAuthMode.values()).filter(e -> e.name().equals(name)).findFirst() + .orElseThrow(() -> new NoSuchElementException("no such auth type")); + } + + public int getCode() { + return code; + } + + public String getDescp() { + return descp; + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftConnectionParam.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftConnectionParam.java index dd3c2d1b6e..f671e026d0 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftConnectionParam.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftConnectionParam.java @@ -19,8 +19,16 @@ package org.apache.dolphinscheduler.plugin.datasource.redshift.param; import org.apache.dolphinscheduler.spi.datasource.BaseConnectionParam; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class RedshiftConnectionParam extends BaseConnectionParam { + protected RedshiftAuthMode mode; + protected String dbUser; + @Override public String toString() { return "RedshiftConnectionParam{" @@ -33,6 +41,8 @@ public class RedshiftConnectionParam extends BaseConnectionParam { + ", driverClassName='" + driverClassName + '\'' + ", validationQuery='" + validationQuery + '\'' + ", other='" + other + '\'' + + ", dbUser='" + dbUser + '\'' + + ", mode='" + mode + '\'' + '}'; } } diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceParamDTO.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceParamDTO.java index e7f5a49246..6fbb4b23e3 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceParamDTO.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceParamDTO.java @@ -20,8 +20,21 @@ package org.apache.dolphinscheduler.plugin.datasource.redshift.param; import org.apache.dolphinscheduler.plugin.datasource.api.datasource.BaseDataSourceParamDTO; import org.apache.dolphinscheduler.spi.enums.DbType; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class RedshiftDataSourceParamDTO extends BaseDataSourceParamDTO { + protected RedshiftAuthMode mode; + protected String dbUser; + + @Override + public DbType getType() { + return DbType.REDSHIFT; + } + @Override public String toString() { return "RedshiftDataSourceParamDTO{" @@ -33,11 +46,8 @@ public class RedshiftDataSourceParamDTO extends BaseDataSourceParamDTO { + ", userName='" + userName + '\'' + ", password='" + password + '\'' + ", other='" + other + '\'' + + ", dbUser='" + dbUser + '\'' + + ", mode='" + mode + '\'' + '}'; } - - @Override - public DbType getType() { - return DbType.REDSHIFT; - } } diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessor.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessor.java index df951f8345..c9163fb5df 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessor.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/main/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessor.java @@ -29,6 +29,7 @@ import org.apache.dolphinscheduler.spi.datasource.ConnectionParam; import org.apache.dolphinscheduler.spi.enums.DbType; import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; import java.sql.Connection; import java.sql.DriverManager; @@ -55,8 +56,25 @@ public class RedshiftDataSourceProcessor extends AbstractDataSourceProcessor { String[] hostPortArray = hostSeperator[hostSeperator.length - 1].split(Constants.COMMA); RedshiftDataSourceParamDTO redshiftDatasourceParamDTO = new RedshiftDataSourceParamDTO(); - redshiftDatasourceParamDTO.setPort(Integer.parseInt(hostPortArray[0].split(Constants.COLON)[1])); - redshiftDatasourceParamDTO.setHost(hostPortArray[0].split(Constants.COLON)[0]); + redshiftDatasourceParamDTO.setMode(connectionParams.getMode()); + redshiftDatasourceParamDTO.setDbUser(connectionParams.getDbUser()); + if (connectionParams.getMode().equals(RedshiftAuthMode.PASSWORD)) { + redshiftDatasourceParamDTO.setPort(Integer.parseInt(hostPortArray[0].split(Constants.COLON)[1])); + redshiftDatasourceParamDTO.setHost(hostPortArray[0].split(Constants.COLON)[0]); + } else { + if (hostPortArray[0].contains(Constants.COLON)) { + String portString = hostPortArray[0].split(Constants.COLON)[1]; + if (StringUtils.isNumeric(portString)) { + redshiftDatasourceParamDTO.setPort(Integer.parseInt(portString)); + redshiftDatasourceParamDTO.setHost(hostPortArray[0].split(Constants.COLON)[0]); + } else { + redshiftDatasourceParamDTO.setHost(hostPortArray[0]); + } + } else { + redshiftDatasourceParamDTO.setHost(hostPortArray[0]); + } + + } redshiftDatasourceParamDTO.setDatabase(connectionParams.getDatabase()); redshiftDatasourceParamDTO.setUserName(connectionParams.getUser()); redshiftDatasourceParamDTO.setOther(connectionParams.getOther()); @@ -67,9 +85,7 @@ public class RedshiftDataSourceProcessor extends AbstractDataSourceProcessor { @Override public BaseConnectionParam createConnectionParams(BaseDataSourceParamDTO datasourceParam) { RedshiftDataSourceParamDTO redshiftParam = (RedshiftDataSourceParamDTO) datasourceParam; - String address = - String.format("%s%s:%s", DataSourceConstants.JDBC_REDSHIFT, redshiftParam.getHost(), - redshiftParam.getPort()); + String address = getAddress(redshiftParam); String jdbcUrl = address + Constants.SLASH + redshiftParam.getDatabase(); RedshiftConnectionParam redshiftConnectionParam = new RedshiftConnectionParam(); @@ -81,6 +97,8 @@ public class RedshiftDataSourceProcessor extends AbstractDataSourceProcessor { redshiftConnectionParam.setDatabase(redshiftParam.getDatabase()); redshiftConnectionParam.setDriverClassName(getDatasourceDriver()); redshiftConnectionParam.setValidationQuery(getValidationQuery()); + redshiftConnectionParam.setMode(redshiftParam.getMode()); + redshiftConnectionParam.setDbUser(redshiftParam.getDbUser()); return redshiftConnectionParam; } @@ -114,8 +132,14 @@ public class RedshiftDataSourceProcessor extends AbstractDataSourceProcessor { public Connection getConnection(ConnectionParam connectionParam) throws ClassNotFoundException, SQLException { RedshiftConnectionParam redshiftConnectionParam = (RedshiftConnectionParam) connectionParam; Class.forName(getDatasourceDriver()); - return DriverManager.getConnection(getJdbcUrl(connectionParam), - redshiftConnectionParam.getUser(), PasswordUtils.decodePassword(redshiftConnectionParam.getPassword())); + if (redshiftConnectionParam.getMode().equals(RedshiftAuthMode.PASSWORD)) { + return DriverManager.getConnection(getJdbcUrl(connectionParam), + redshiftConnectionParam.getUser(), + PasswordUtils.decodePassword(redshiftConnectionParam.getPassword())); + } else if (redshiftConnectionParam.getMode().equals(RedshiftAuthMode.IAM_ACCESS_KEY)) { + return getConnectionByIAM(redshiftConnectionParam); + } + return null; } @Override @@ -128,7 +152,34 @@ public class RedshiftDataSourceProcessor extends AbstractDataSourceProcessor { return new RedshiftDataSourceProcessor(); } - private String transformOther(Map otherMap) { + /** + * 2 auth mode + * PASSWORD: address example: jdbc:redshift://examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439 + * IAM_ACCESS_KEY: + * address example1: jdbc:redshift:iam://examplecluster:us-west-2 + * address example2: jdbc:redshift:iam://examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439 + * + * @param redshiftParam + * @return + */ + private String getAddress(RedshiftDataSourceParamDTO redshiftParam) { + if (redshiftParam.getMode().equals(RedshiftAuthMode.PASSWORD)) { + return String.format("%s%s:%s", DataSourceConstants.JDBC_REDSHIFT, redshiftParam.getHost(), + redshiftParam.getPort()); + } else if (redshiftParam.getMode().equals(RedshiftAuthMode.IAM_ACCESS_KEY)) { + if (redshiftParam.getPort() == null) { + // construct IAM_ACCESS_KEY example 1 format + return String.format("%s%s", DataSourceConstants.JDBC_REDSHIFT_IAM, redshiftParam.getHost()); + } else { + // construct IAM_ACCESS_KEY example 2 format + return String.format("%s%s:%s", DataSourceConstants.JDBC_REDSHIFT_IAM, redshiftParam.getHost(), + redshiftParam.getPort()); + } + } + return null; + } + + private static String transformOther(Map otherMap) { if (MapUtils.isNotEmpty(otherMap)) { List list = new ArrayList<>(otherMap.size()); otherMap.forEach((key, value) -> list.add(String.format("%s=%s", key, value))); @@ -137,4 +188,42 @@ public class RedshiftDataSourceProcessor extends AbstractDataSourceProcessor { return null; } + /** + * 2 auth mode + * PASSWORD: address example: jdbc:redshift://examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439/dev + * IAM_ACCESS_KEY: + * address example1: jdbc:redshift:iam://examplecluster:us-west-2/dev?AccessKeyID=xxx&SecretAccessKey=xxx&DbUser=kristen + * address example2: jdbc:redshift:iam://examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439/dev?AccessKeyID=xxx&SecretAccessKey=xxx&DbUser=kristen + * + * @param redshiftConnectionParam + * @return + */ + public static Connection getConnectionByIAM(RedshiftConnectionParam redshiftConnectionParam) { + String basic; + String authParams = String.format("AccessKeyID=%s&SecretAccessKey=%s&DbUser=%s", + redshiftConnectionParam.getUser(), PasswordUtils.decodePassword(redshiftConnectionParam.getPassword()), + redshiftConnectionParam.getDbUser()); + String connectionUrl; + if (MapUtils.isNotEmpty(redshiftConnectionParam.getOther())) { + basic = String.format("%s?%s", redshiftConnectionParam.getJdbcUrl(), + transformOther(redshiftConnectionParam.getOther())); + // if have other params map, basic will be + // 'jdbc:redshift:iam://examplecluster:us-west-2/dev?param1=xx¶m2=xx' + // append AccessKeyID &SecretAccessKey &DbUser + connectionUrl = String.format("%s&%s", basic, authParams); + } else { + basic = redshiftConnectionParam.getJdbcUrl(); + // if none other params map, basic will be 'jdbc:redshift:iam://examplecluster:us-west-2/dev' + // append AccessKeyID &SecretAccessKey &DbUser + connectionUrl = String.format("%s?%s", basic, authParams); + } + try { + Class.forName(DataSourceConstants.COM_REDSHIFT_JDBC_DRIVER); + return DriverManager.getConnection(connectionUrl); + } catch (SQLException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } } diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/test/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessorTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/test/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessorTest.java index 1c752667b4..2e61f71536 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/test/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessorTest.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-redshift/src/test/java/org/apache/dolphinscheduler/plugin/datasource/redshift/param/RedshiftDataSourceProcessorTest.java @@ -49,7 +49,7 @@ public class RedshiftDataSourceProcessorTest { redshiftDatasourceParamDTO.setUserName("awsuser"); redshiftDatasourceParamDTO.setPassword("123456"); redshiftDatasourceParamDTO.setOther(props); - + redshiftDatasourceParamDTO.setMode(RedshiftAuthMode.PASSWORD); try (MockedStatic mockedStaticPasswordUtils = Mockito.mockStatic(PasswordUtils.class)) { mockedStaticPasswordUtils.when(() -> PasswordUtils.encodePassword(Mockito.anyString())).thenReturn("test"); RedshiftConnectionParam connectionParams = (RedshiftConnectionParam) redshiftDatasourceProcessor @@ -63,7 +63,7 @@ public class RedshiftDataSourceProcessorTest { public void testCreateConnectionParams2() { String connectionJson = "{\"user\":\"awsuser\",\"password\":\"123456\",\"address\":\"jdbc:redshift://localhost:5439\"" - + ",\"database\":\"dev\",\"jdbcUrl\":\"jdbc:redshift://localhost:5439/dev\"}"; + + ",\"database\":\"dev\",\"jdbcUrl\":\"jdbc:redshift://localhost:5439/dev\",\"mode\":\"password\"}"; RedshiftConnectionParam connectionParams = (RedshiftConnectionParam) redshiftDatasourceProcessor .createConnectionParams(connectionJson); Assertions.assertNotNull(connectionParams); diff --git a/dolphinscheduler-ui/src/locales/en_US/datasource.ts b/dolphinscheduler-ui/src/locales/en_US/datasource.ts index 6c1e48eee8..dfa8e6d01d 100644 --- a/dolphinscheduler-ui/src/locales/en_US/datasource.ts +++ b/dolphinscheduler-ui/src/locales/en_US/datasource.ts @@ -86,4 +86,10 @@ export default { clientSecret: 'ClientSecret', OAuth_token_endpoint: 'OAuth 2.0 token endpoint', endpoint_tips: 'Please enter OAuth Token', + AccessKeyID:'AccessKeyID', + AccessKeyID_tips:'Please input AccessKeyID', + SecretAccessKey:'SecretAccessKey', + SecretAccessKey_tips:'Please input SecretAccessKey', + dbUser:'DbUser', + dbUser_tips:'Please input DbUser', } diff --git a/dolphinscheduler-ui/src/locales/zh_CN/datasource.ts b/dolphinscheduler-ui/src/locales/zh_CN/datasource.ts index 48c29992e3..749647270d 100644 --- a/dolphinscheduler-ui/src/locales/zh_CN/datasource.ts +++ b/dolphinscheduler-ui/src/locales/zh_CN/datasource.ts @@ -82,5 +82,11 @@ export default { clientId: 'ClientId', clientSecret: 'ClientSecret', OAuth_token_endpoint: 'OAuth 2.0 token endpoint', - endpoint_tips: '请输入OAuth' + endpoint_tips: '请输入OAuth', + AccessKeyID:'AccessKeyID', + AccessKeyID_tips:'请输入AccessKeyID', + SecretAccessKey:'SecretAccessKey', + SecretAccessKey_tips:'请输入SecretAccessKey', + dbUser:'DbUser', + dbUser_tips:'请输入DbUser', } diff --git a/dolphinscheduler-ui/src/service/modules/data-source/types.ts b/dolphinscheduler-ui/src/service/modules/data-source/types.ts index 0d0b3e8867..aa43909bc4 100644 --- a/dolphinscheduler-ui/src/service/modules/data-source/types.ts +++ b/dolphinscheduler-ui/src/service/modules/data-source/types.ts @@ -72,6 +72,7 @@ interface IDataSource { bindTestId?: number endpoint?: string MSIClientId?: string + dbUser?: string } interface ListReq { diff --git a/dolphinscheduler-ui/src/views/datasource/list/detail.tsx b/dolphinscheduler-ui/src/views/datasource/list/detail.tsx index bb46683265..0d9dd6235a 100644 --- a/dolphinscheduler-ui/src/views/datasource/list/detail.tsx +++ b/dolphinscheduler-ui/src/views/datasource/list/detail.tsx @@ -162,6 +162,7 @@ const DetailModal = defineComponent({ showPrincipal, showMode, modeOptions, + redShitModeOptions, loading, saving, testing, @@ -245,7 +246,9 @@ const DetailModal = defineComponent({ v-show={showPort} label={t('datasource.port')} path='port' - show-require-mark + show-require-mark={ + !(showMode && detailForm.mode === 'IAM-accessKey') + } > {/* SqlPassword */} @@ -427,10 +430,47 @@ const DetailModal = defineComponent({ placeholder={t('datasource.OAuth_token_endpoint')} /> - - - - + + + + + + + + + diff --git a/dolphinscheduler-ui/src/views/datasource/list/use-form.ts b/dolphinscheduler-ui/src/views/datasource/list/use-form.ts index dcf15e766c..0159905a34 100644 --- a/dolphinscheduler-ui/src/views/datasource/list/use-form.ts +++ b/dolphinscheduler-ui/src/views/datasource/list/use-form.ts @@ -54,7 +54,8 @@ export function useForm(id?: number) { testFlag: -1, bindTestId: undefined, endpoint: '', - MSIClientId: '' + MSIClientId: '', + dbUser: '' } as IDataSourceDetail const state = reactive({ @@ -88,6 +89,9 @@ export function useForm(id?: number) { port: { trigger: ['input'], validator() { + if (state.showMode && state.detailForm.mode === 'IAM-accessKey') { + return + } if (!state.detailForm.port && state.showPort) { return new Error(t('datasource.port_tips')) } @@ -180,6 +184,18 @@ export function useForm(id?: number) { } } }, + dbUser: { + trigger: ['input'], + validator() { + if ( + !state.detailForm.dbUser && + state.showMode && + state.detailForm.mode === 'IAM-accessKey' + ) { + return new Error(t('datasource.IAM-accessKey')) + } + } + } // databaseUserName: { // trigger: ['input'], // validator() { @@ -210,6 +226,16 @@ export function useForm(id?: number) { label: "accessToken", value: 'accessToken', }, + ], + redShitModeOptions: [ + { + label: 'password', + value: 'password' + }, + { + label: 'IAM-accessKey', + value: 'IAM-accessKey' + } ] }) @@ -222,7 +248,7 @@ export function useForm(id?: number) { state.showHost = type !== 'ATHENA' state.showPort = type !== 'ATHENA' state.showAwsRegion = type === 'ATHENA' - state.showMode = type === 'AZURESQL' + state.showMode = ['AZURESQL', 'REDSHIFT'].includes(type) if (type === 'ORACLE' && !id) { state.detailForm.connectType = 'ORACLE_SERVICE_NAME'