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

renqs pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-cdc.git


The following commit(s) were added to refs/heads/master by this push:
     new 21032263c [FLINK-34969][cdc][cli] Add support for both new and old 
Flink config files in Flink CDC (#3194)
21032263c is described below

commit 21032263c54b9b14999dc234fea551b7253c6e4f
Author: sky <[email protected]>
AuthorDate: Tue Apr 23 17:58:08 2024 +0800

    [FLINK-34969][cdc][cli] Add support for both new and old Flink config files 
in Flink CDC (#3194)
---
 flink-cdc-cli/pom.xml                              |   8 +-
 .../java/org/apache/flink/cdc/cli/CliFrontend.java |   4 +-
 .../flink/cdc/cli/utils/ConfigurationUtils.java    |  54 ++--
 .../flink/cdc/cli/utils/FlinkEnvironmentUtils.java |  19 +-
 .../flink/cdc/cli/utils/YamlParserUtils.java       | 293 +++++++++++++++++++++
 .../cdc/cli/utils/ConfigurationUtilsTest.java      |  87 ++++++
 .../src/test/resources/flink-home/conf/config.yaml |  41 +++
 7 files changed, 478 insertions(+), 28 deletions(-)

diff --git a/flink-cdc-cli/pom.xml b/flink-cdc-cli/pom.xml
index 285462031..1aa57f336 100644
--- a/flink-cdc-cli/pom.xml
+++ b/flink-cdc-cli/pom.xml
@@ -29,6 +29,7 @@ limitations under the License.
 
     <properties>
         <commons-cli.version>1.6.0</commons-cli.version>
+        <snakeyaml.version>2.6</snakeyaml.version>
     </properties>
 
     <dependencies>
@@ -37,7 +38,12 @@ limitations under the License.
             <artifactId>flink-cdc-common</artifactId>
             <version>${project.version}</version>
         </dependency>
-
+        <!-- YAML parser utilities -->
+        <dependency>
+            <groupId>org.snakeyaml</groupId>
+            <artifactId>snakeyaml-engine</artifactId>
+            <version>${snakeyaml.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.flink</groupId>
             <artifactId>flink-cdc-composer</artifactId>
diff --git 
a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/CliFrontend.java 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/CliFrontend.java
index 663f03942..c67c3cf99 100644
--- a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/CliFrontend.java
+++ b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/CliFrontend.java
@@ -169,7 +169,7 @@ public class CliFrontend {
         if (globalConfig != null) {
             Path globalConfigPath = Paths.get(globalConfig);
             LOG.info("Using global config in command line: {}", 
globalConfigPath);
-            return ConfigurationUtils.loadMapFormattedConfig(globalConfigPath);
+            return ConfigurationUtils.loadConfigFile(globalConfigPath);
         }
 
         // Fallback to Flink CDC home
@@ -178,7 +178,7 @@ public class CliFrontend {
             Path globalConfigPath =
                     
Paths.get(flinkCdcHome).resolve("conf").resolve("flink-cdc.yaml");
             LOG.info("Using global config in FLINK_CDC_HOME: {}", 
globalConfigPath);
-            return ConfigurationUtils.loadMapFormattedConfig(globalConfigPath);
+            return ConfigurationUtils.loadConfigFile(globalConfigPath);
         }
 
         // Fallback to empty configuration
diff --git 
a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/ConfigurationUtils.java
 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/ConfigurationUtils.java
index efe0a5143..38fa608f5 100644
--- 
a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/ConfigurationUtils.java
+++ 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/ConfigurationUtils.java
@@ -19,33 +19,41 @@ package org.apache.flink.cdc.cli.utils;
 
 import org.apache.flink.cdc.common.configuration.Configuration;
 
-import 
org.apache.flink.shaded.jackson2.com.fasterxml.jackson.core.type.TypeReference;
-import 
org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectMapper;
-import 
org.apache.flink.shaded.jackson2.com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-
-import java.io.FileNotFoundException;
-import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /** Utilities for handling {@link Configuration}. */
 public class ConfigurationUtils {
-    public static Configuration loadMapFormattedConfig(Path configPath) throws 
Exception {
-        if (!Files.exists(configPath)) {
-            throw new FileNotFoundException(
-                    String.format("Cannot find configuration file at \"%s\"", 
configPath));
-        }
-        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
-        try {
-            Map<String, String> configMap =
-                    mapper.readValue(
-                            configPath.toFile(), new TypeReference<Map<String, 
String>>() {});
-            return Configuration.fromMap(configMap);
-        } catch (Exception e) {
-            throw new IllegalStateException(
-                    String.format(
-                            "Failed to load config file \"%s\" to key-value 
pairs", configPath),
-                    e);
-        }
+
+    private static final String KEY_SEPARATOR = ".";
+
+    public static Configuration loadConfigFile(Path configPath) throws 
Exception {
+        Map<String, Object> configMap = 
YamlParserUtils.loadYamlFile(configPath.toFile());
+        return Configuration.fromMap(flattenConfigMap(configMap, ""));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Map<String, String> flattenConfigMap(
+            Map<String, Object> config, String keyPrefix) {
+        final Map<String, String> flattenedMap = new HashMap<>();
+
+        config.forEach(
+                (key, value) -> {
+                    String flattenedKey = keyPrefix + key;
+                    if (value instanceof Map) {
+                        Map<String, Object> e = (Map<String, Object>) value;
+                        flattenedMap.putAll(flattenConfigMap(e, flattenedKey + 
KEY_SEPARATOR));
+                    } else {
+                        if (value instanceof List) {
+                            flattenedMap.put(flattenedKey, 
YamlParserUtils.toYAMLString(value));
+                        } else {
+                            flattenedMap.put(flattenedKey, value.toString());
+                        }
+                    }
+                });
+
+        return flattenedMap;
     }
 }
diff --git 
a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/FlinkEnvironmentUtils.java
 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/FlinkEnvironmentUtils.java
index 4880fa47b..d250ea835 100644
--- 
a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/FlinkEnvironmentUtils.java
+++ 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/FlinkEnvironmentUtils.java
@@ -21,18 +21,33 @@ import 
org.apache.flink.cdc.common.configuration.Configuration;
 import org.apache.flink.cdc.composer.flink.FlinkPipelineComposer;
 import org.apache.flink.runtime.jobgraph.SavepointRestoreSettings;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileNotFoundException;
 import java.nio.file.Path;
 import java.util.List;
 
 /** Utilities for handling Flink configuration and environment. */
 public class FlinkEnvironmentUtils {
 
+    private static final Logger LOG = 
LoggerFactory.getLogger(FlinkEnvironmentUtils.class);
     private static final String FLINK_CONF_DIR = "conf";
-    private static final String FLINK_CONF_FILENAME = "flink-conf.yaml";
+    private static final String LEGACY_FLINK_CONF_FILENAME = "flink-conf.yaml";
+    private static final String FLINK_CONF_FILENAME = "config.yaml";
 
     public static Configuration loadFlinkConfiguration(Path flinkHome) throws 
Exception {
         Path flinkConfPath = 
flinkHome.resolve(FLINK_CONF_DIR).resolve(FLINK_CONF_FILENAME);
-        return ConfigurationUtils.loadMapFormattedConfig(flinkConfPath);
+        try {
+            return ConfigurationUtils.loadConfigFile(flinkConfPath);
+        } catch (FileNotFoundException e) {
+            LOG.warn(
+                    "Failed to load the configuration file from {}. Trying to 
use legacy YAML parser to load flink configuration file from {}.",
+                    FLINK_CONF_FILENAME,
+                    LEGACY_FLINK_CONF_FILENAME);
+            return ConfigurationUtils.loadConfigFile(
+                    
flinkHome.resolve(FLINK_CONF_DIR).resolve(LEGACY_FLINK_CONF_FILENAME));
+        }
     }
 
     public static FlinkPipelineComposer createComposer(
diff --git 
a/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/YamlParserUtils.java
 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/YamlParserUtils.java
new file mode 100644
index 000000000..d47332b01
--- /dev/null
+++ 
b/flink-cdc-cli/src/main/java/org/apache/flink/cdc/cli/utils/YamlParserUtils.java
@@ -0,0 +1,293 @@
+/*
+ * 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.flink.cdc.cli.utils;
+
+import org.apache.flink.cdc.common.utils.TimeUtils;
+import org.apache.flink.configuration.MemorySize;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snakeyaml.engine.v2.api.Dump;
+import org.snakeyaml.engine.v2.api.DumpSettings;
+import org.snakeyaml.engine.v2.api.Load;
+import org.snakeyaml.engine.v2.api.LoadSettings;
+import org.snakeyaml.engine.v2.common.FlowStyle;
+import org.snakeyaml.engine.v2.exceptions.Mark;
+import org.snakeyaml.engine.v2.exceptions.MarkedYamlEngineException;
+import org.snakeyaml.engine.v2.exceptions.YamlEngineException;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.ScalarNode;
+import org.snakeyaml.engine.v2.nodes.Tag;
+import org.snakeyaml.engine.v2.representer.StandardRepresenter;
+import org.snakeyaml.engine.v2.schema.CoreSchema;
+
+import javax.annotation.Nonnull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * This class contains utility methods to load standard yaml file and convert 
object to standard
+ * yaml syntax.
+ */
+public class YamlParserUtils {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(YamlParserUtils.class);
+
+    private static final DumpSettings blockerDumperSettings =
+            DumpSettings.builder()
+                    .setDefaultFlowStyle(FlowStyle.BLOCK)
+                    // Disable split long lines to avoid add unexpected line 
breaks
+                    .setSplitLines(false)
+                    .setSchema(new CoreSchema())
+                    .build();
+
+    private static final DumpSettings flowDumperSettings =
+            DumpSettings.builder()
+                    .setDefaultFlowStyle(FlowStyle.FLOW)
+                    // Disable split long lines to avoid add unexpected line 
breaks
+                    .setSplitLines(false)
+                    .setSchema(new CoreSchema())
+                    .build();
+
+    private static final Dump blockerDumper =
+            new Dump(blockerDumperSettings, new 
FlinkConfigRepresenter(blockerDumperSettings));
+
+    private static final Dump flowDumper =
+            new Dump(flowDumperSettings, new 
FlinkConfigRepresenter(flowDumperSettings));
+
+    private static final Load loader =
+            new Load(LoadSettings.builder().setSchema(new 
CoreSchema()).build());
+
+    /**
+     * Loads the contents of the given YAML file into a map.
+     *
+     * @param file the YAML file to load.
+     * @return a non-null map representing the YAML content. If the file is 
empty or only contains
+     *     comments, an empty map is returned.
+     * @throws FileNotFoundException if the YAML file is not found.
+     * @throws YamlEngineException if the file cannot be parsed.
+     * @throws IOException if an I/O error occurs while reading from the file 
stream.
+     */
+    @SuppressWarnings("unchecked")
+    public static synchronized @Nonnull Map<String, Object> loadYamlFile(File 
file)
+            throws Exception {
+        try (FileInputStream inputStream = new FileInputStream((file))) {
+            Map<String, Object> yamlResult =
+                    (Map<String, Object>) 
loader.loadFromInputStream(inputStream);
+            return yamlResult == null ? new HashMap<>() : yamlResult;
+        } catch (FileNotFoundException e) {
+            LOG.error("Failed to find YAML file", e);
+            throw e;
+        } catch (IOException | YamlEngineException e) {
+            if (e instanceof MarkedYamlEngineException) {
+                YamlEngineException exception =
+                        
wrapExceptionToHiddenSensitiveData((MarkedYamlEngineException) e);
+                LOG.error("Failed to parse YAML configuration", exception);
+                throw exception;
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Converts the given value to a string representation in the YAML syntax. 
This method uses a
+     * YAML parser to convert the object to YAML format.
+     *
+     * <p>The resulting YAML string may have line breaks at the end of each 
line. This method
+     * removes the line break at the end of the string if it exists.
+     *
+     * <p>Note: This method may perform escaping on certain characters in the 
value to ensure proper
+     * YAML syntax.
+     *
+     * @param value The value to be converted.
+     * @return The string representation of the value in YAML syntax.
+     */
+    public static synchronized String toYAMLString(Object value) {
+        try {
+            String output = flowDumper.dumpToString(value);
+            // remove the line break
+            String linebreak = flowDumperSettings.getBestLineBreak();
+            if (output.endsWith(linebreak)) {
+                output = output.substring(0, output.length() - 
linebreak.length());
+            }
+            return output;
+        } catch (MarkedYamlEngineException exception) {
+            throw wrapExceptionToHiddenSensitiveData(exception);
+        }
+    }
+
+    /**
+     * Converts a flat map into a nested map structure and outputs the result 
as a list of
+     * YAML-formatted strings. Each item in the list represents a single line 
of the YAML data. The
+     * method is synchronized and thus thread-safe.
+     *
+     * @param flattenMap A map containing flattened keys (e.g., 
"parent.child.key") associated with
+     *     their values.
+     * @return A list of strings that represents the YAML data, where each 
item corresponds to a
+     *     line of the data.
+     */
+    @SuppressWarnings("unchecked")
+    public static synchronized List<String> convertAndDumpYamlFromFlatMap(
+            Map<String, Object> flattenMap) {
+        try {
+            Map<String, Object> nestedMap = new LinkedHashMap<>();
+            for (Map.Entry<String, Object> entry : flattenMap.entrySet()) {
+                String[] keys = entry.getKey().split("\\.");
+                Map<String, Object> currentMap = nestedMap;
+                for (int i = 0; i < keys.length - 1; i++) {
+                    currentMap =
+                            (Map<String, Object>)
+                                    currentMap.computeIfAbsent(keys[i], k -> 
new LinkedHashMap<>());
+                }
+                currentMap.put(keys[keys.length - 1], entry.getValue());
+            }
+            String data = blockerDumper.dumpToString(nestedMap);
+            String linebreak = blockerDumperSettings.getBestLineBreak();
+            return Arrays.asList(data.split(linebreak));
+        } catch (MarkedYamlEngineException exception) {
+            throw wrapExceptionToHiddenSensitiveData(exception);
+        }
+    }
+
+    public static synchronized <T> T convertToObject(String value, Class<T> 
type) {
+        try {
+            return type.cast(loader.loadFromString(value));
+        } catch (MarkedYamlEngineException exception) {
+            throw wrapExceptionToHiddenSensitiveData(exception);
+        }
+    }
+
+    /**
+     * This method wraps a MarkedYAMLException to hide sensitive data in its 
message. Before using
+     * this method, an exception message might include sensitive information 
like:
+     *
+     * <pre>{@code
+     * while constructing a mapping
+     * in 'reader', line 1, column 1:
+     *     key1: secret1
+     *     ^
+     * found duplicate key key1
+     * in 'reader', line 2, column 1:
+     *     key1: secret2
+     *     ^
+     * }</pre>
+     *
+     * <p>After using this method, the message will be sanitized to hide the 
sensitive details:
+     *
+     * <pre>{@code
+     * while constructing a mapping
+     * in 'reader', line 1, column 1
+     * found duplicate key key1
+     * in 'reader', line 2, column 1
+     * }</pre>
+     *
+     * @param exception The MarkedYamlEngineException containing potentially 
sensitive data.
+     * @return A YamlEngineException with a message that has sensitive data 
hidden.
+     */
+    private static YamlEngineException wrapExceptionToHiddenSensitiveData(
+            MarkedYamlEngineException exception) {
+        StringBuilder lines = new StringBuilder();
+        String context = exception.getContext();
+        Optional<Mark> contextMark = exception.getContextMark();
+        Optional<Mark> problemMark = exception.getProblemMark();
+        String problem = exception.getProblem();
+
+        if (context != null) {
+            lines.append(context);
+            lines.append("\n");
+        }
+
+        if (contextMark.isPresent()
+                && (problem == null
+                        || !problemMark.isPresent()
+                        || 
contextMark.get().getName().equals(problemMark.get().getName())
+                        || contextMark.get().getLine() != 
problemMark.get().getLine()
+                        || contextMark.get().getColumn() != 
problemMark.get().getColumn())) {
+            lines.append(hiddenSensitiveDataInMark(contextMark.get()));
+            lines.append("\n");
+        }
+
+        if (problem != null) {
+            lines.append(problem);
+            lines.append("\n");
+        }
+
+        if (problemMark.isPresent()) {
+            lines.append(hiddenSensitiveDataInMark(problemMark.get()));
+            lines.append("\n");
+        }
+
+        Throwable cause = exception.getCause();
+        if (cause instanceof MarkedYamlEngineException) {
+            cause = 
wrapExceptionToHiddenSensitiveData((MarkedYamlEngineException) cause);
+        }
+
+        YamlEngineException yamlException = new 
YamlEngineException(lines.toString(), cause);
+        yamlException.setStackTrace(exception.getStackTrace());
+        return yamlException;
+    }
+
+    /**
+     * This method is a mock implementation of the Mark#toString() method, 
specifically designed to
+     * exclude the Mark#get_snippet(), to prevent leaking any sensitive data.
+     */
+    private static String hiddenSensitiveDataInMark(Mark mark) {
+        return " in "
+                + mark.getName()
+                + ", line "
+                + (mark.getLine() + 1)
+                + ", column "
+                + (mark.getColumn() + 1);
+    }
+
+    private static class FlinkConfigRepresenter extends StandardRepresenter {
+        public FlinkConfigRepresenter(DumpSettings dumpSettings) {
+            super(dumpSettings);
+            representers.put(Duration.class, this::representDuration);
+            representers.put(MemorySize.class, this::representMemorySize);
+            parentClassRepresenters.put(Enum.class, this::representEnum);
+        }
+
+        private Node representDuration(Object data) {
+            Duration duration = (Duration) data;
+            String durationString = TimeUtils.formatWithHighestUnit(duration);
+            return new ScalarNode(Tag.STR, durationString, 
settings.getDefaultScalarStyle());
+        }
+
+        private Node representMemorySize(Object data) {
+            MemorySize memorySize = (MemorySize) data;
+            return new ScalarNode(Tag.STR, memorySize.toString(), 
settings.getDefaultScalarStyle());
+        }
+
+        private Node representEnum(Object data) {
+            return new ScalarNode(Tag.STR, data.toString(), 
settings.getDefaultScalarStyle());
+        }
+    }
+}
diff --git 
a/flink-cdc-cli/src/test/java/org/apache/flink/cdc/cli/utils/ConfigurationUtilsTest.java
 
b/flink-cdc-cli/src/test/java/org/apache/flink/cdc/cli/utils/ConfigurationUtilsTest.java
new file mode 100644
index 000000000..7e012fce9
--- /dev/null
+++ 
b/flink-cdc-cli/src/test/java/org/apache/flink/cdc/cli/utils/ConfigurationUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.flink.cdc.cli.utils;
+
+import org.apache.flink.cdc.common.configuration.ConfigOption;
+import org.apache.flink.cdc.common.configuration.ConfigOptions;
+import org.apache.flink.cdc.common.configuration.Configuration;
+
+import org.apache.flink.shaded.curator5.com.google.common.io.Resources;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** Unit test for {@link org.apache.flink.cdc.cli.utils.ConfigurationUtils}. */
+class ConfigurationUtilsTest {
+
+    private static final Map<ConfigOption<?>, Object> CONFIG_OPTIONS = new 
HashMap<>();
+
+    static {
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("jobmanager.rpc.address").stringType().noDefaultValue(),
+                "localhost");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("jobmanager.rpc.port").stringType().noDefaultValue(), "6123");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("jobmanager.bind-host").stringType().noDefaultValue(),
+                "localhost");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("jobmanager.memory.process.size").stringType().noDefaultValue(),
+                "1600m");
+        CONFIG_OPTIONS.put(
+                ConfigOptions.key("jobmanager.execution.failover-strategy")
+                        .stringType()
+                        .noDefaultValue(),
+                "region");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("taskmanager.bind-host").stringType().noDefaultValue(),
+                "localhost");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("taskmanager.host").stringType().noDefaultValue(), 
"localhost");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("taskmanager.memory.process.size").stringType().noDefaultValue(),
+                "1728m");
+        CONFIG_OPTIONS.put(
+                
ConfigOptions.key("taskmanager.numberOfTaskSlots").stringType().noDefaultValue(),
+                "1");
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"flink-home/conf/config.yaml", 
"flink-home/conf/flink-conf.yaml"})
+    void loadConfigFile(String resourcePath) throws Exception {
+        URL resource = Resources.getResource(resourcePath);
+        Path path = Paths.get(resource.toURI());
+        Configuration configuration = ConfigurationUtils.loadConfigFile(path);
+        Map<String, String> configMap = configuration.toMap();
+        for (Map.Entry<ConfigOption<?>, Object> entry : 
CONFIG_OPTIONS.entrySet()) {
+            String key = entry.getKey().key();
+            Object expectedValue = entry.getValue();
+            assertTrue(configMap.containsKey(key));
+            assertEquals(expectedValue, configMap.get(key));
+        }
+    }
+}
diff --git a/flink-cdc-cli/src/test/resources/flink-home/conf/config.yaml 
b/flink-cdc-cli/src/test/resources/flink-home/conf/config.yaml
new file mode 100644
index 000000000..30c44f547
--- /dev/null
+++ b/flink-cdc-cli/src/test/resources/flink-home/conf/config.yaml
@@ -0,0 +1,41 @@
+#  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.
+################################################################################
+
+jobmanager:
+  rpc:
+    address: localhost
+    port: 6123
+  bind-host: localhost
+  memory:
+    process:
+      size: 1600m
+  execution:
+    failover-strategy: region
+
+taskmanager:
+  bind-host: localhost
+  host: localhost
+  memory:
+    process:
+      size: 1728m
+  numberOfTaskSlots: 1
+
+
+parallelism:
+  default: 1
+
+# The rest of your YAML file...

Reply via email to