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

vveider pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new da0a7f73f IGNITE-17130 Add CLI profile API implementation (#897)
da0a7f73f is described below

commit da0a7f73f9eef9c0ae43a4a3ba1d8b663236b269
Author: Mikhail <pochat...@users.noreply.github.com>
AuthorDate: Mon Jul 11 17:53:55 2022 +0300

    IGNITE-17130 Add CLI profile API implementation (#897)
---
 modules/cli/pom.xml                                |   7 ++
 .../commands/CliCommandTestIntegrationBase.java    |  12 ++
 .../src/main/java/org/apache/ignite/cli/Main.java  |   5 +-
 .../ignite/cli/call/cliconfig/CliConfigCall.java   |  16 ++-
 .../cli/call/cliconfig/CliConfigGetCall.java       |  16 ++-
 ...etCallInput.java => CliConfigGetCallInput.java} |  22 ++--
 .../cli/call/cliconfig/CliConfigSetCall.java       |  16 +--
 .../cli/call/cliconfig/CliConfigSetCallInput.java  |   9 +-
 .../profile/CliConfigCreateProfileCall.java        |  58 ++++++++++
 .../profile/CliConfigCreateProfileCallInput.java   |  84 ++++++++++++++
 .../CliConfigShowProfileCall.java}                 |  17 +--
 .../CliConfigUseCall.java}                         |  19 ++--
 .../commands/cliconfig/CliConfigGetSubCommand.java |   8 +-
 .../commands/cliconfig/CliConfigSetSubCommand.java |   6 +-
 .../commands/cliconfig/CliConfigSubCommand.java    |  12 +-
 .../CliConfigCreateProfileCommand.java}            |  38 ++++---
 .../CliConfigProfileCommand.java}                  |  30 ++---
 .../CliConfigShowProfileCommand.java}              |  23 ++--
 .../cli/commands/connect/ConnectCommand.java       |   3 +-
 .../cli/commands/decorators/ConfigDecorator.java   |  23 ++--
 ...ctory.java => CachedConfigManagerProvider.java} |  16 +--
 .../java/org/apache/ignite/cli/config/Config.java  | 123 ---------------------
 .../apache/ignite/cli/config/ConfigConstants.java  |  52 +++++++++
 .../ConfigDefaultValueProvider.java}               |  17 ++-
 ...ory.java => ConfigInitializationException.java} |  20 ++--
 .../{ConfigFactory.java => ConfigManager.java}     |  25 +++--
 ...nfigFactory.java => ConfigManagerProvider.java} |  13 +--
 ...figFactory.java => ConfigStoringException.java} |  19 ++--
 .../config/{ConfigFactory.java => Profile.java}    |  25 +++--
 ...gFactory.java => ProfileNotFoundException.java} |  13 +--
 ...ConfigFactory.java => StateFolderProvider.java} |  35 ++++--
 .../ignite/cli/config/ini/IniConfigManager.java    | 123 +++++++++++++++++++++
 .../org/apache/ignite/cli/config/ini/IniFile.java  |  98 ++++++++++++++++
 .../apache/ignite/cli/config/ini/IniParser.java    |  99 +++++++++++++++++
 .../apache/ignite/cli/config/ini/IniProfile.java   |  72 ++++++++++++
 .../apache/ignite/cli/config/ini/IniSection.java   |  63 +++++++++++
 .../SectionAlreadyExistsException.java}            |  15 +--
 .../handler/ConfigStoringExceptionHandler.java     |  43 +++++++
 .../handler/DefaultExceptionHandlers.java          |   3 +
 ...s.java => ProfileNotFoundExceptionHandler.java} |  26 ++---
 ...a => SectionAlreadyExistsExceptionHandler.java} |  26 ++---
 .../cli/core/repl/SessionDefaultValueProvider.java |  14 +--
 .../cli/core/repl/executor/ReplExecutor.java       |   4 +-
 .../ignite/cli/commands/CliCommandTestBase.java    |  12 +-
 .../apache/ignite/cli/commands/PicocliBugTest.java |  88 +++++++++++++++
 .../cliconfig/CliConfigActivateCommandTest.java    |  68 ++++++++++++
 .../CliConfigCreateProfileCommandTest.java         | 118 ++++++++++++++++++++
 .../cliconfig/CliConfigGetSubCommandTest.java      |  35 +++++-
 .../cliconfig/CliConfigSetSubCommandTest.java      |  54 ++++++++-
 ...t.java => CliConfigShowProfileCommandTest.java} |  35 ++++--
 .../cliconfig/CliConfigSubCommandTest.java         |  33 +++++-
 .../cli/commands/cliconfig/ConfigManagerTest.java  |  74 +++++++++++++
 .../cli/commands/cliconfig/TestConfigFactory.java  |  50 ---------
 .../cliconfig/TestConfigManagerHelper.java         |  70 ++++++++++++
 ...figTest.java => TestConfigManagerProvider.java} |  28 ++---
 modules/cli/src/test/resources/empty.ini           |   0
 .../cli/src/test/resources/integration_tests.ini   |   6 +
 .../test/resources/two_section_with_internal.ini   |  12 ++
 .../resources/two_section_without_internal.ini     |  10 ++
 parent/pom.xml                                     |   2 +-
 60 files changed, 1620 insertions(+), 443 deletions(-)

diff --git a/modules/cli/pom.xml b/modules/cli/pom.xml
index 20699e8d2..d7794ce20 100644
--- a/modules/cli/pom.xml
+++ b/modules/cli/pom.xml
@@ -259,6 +259,13 @@
 
 
     <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+                <filtering>true</filtering>
+            </testResource>
+        </testResources>
+
         <resources>
             <resource>
                 <directory>src/main/resources</directory>
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java
index 363787cfd..7d3a86ce0 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java
@@ -25,6 +25,10 @@ import jakarta.inject.Inject;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import org.apache.ignite.cli.IntegrationTestBase;
+import org.apache.ignite.cli.commands.cliconfig.TestConfigManagerHelper;
+import org.apache.ignite.cli.commands.cliconfig.TestConfigManagerProvider;
+import org.apache.ignite.cli.config.ConfigDefaultValueProvider;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.TestInfo;
 import picocli.CommandLine;
@@ -39,6 +43,12 @@ public class CliCommandTestIntegrationBase extends 
IntegrationTestBase {
     @Inject
     private ApplicationContext context;
 
+    @Inject
+    ConfigDefaultValueProvider configDefaultValueProvider;
+
+    @Inject
+    TestConfigManagerProvider configManagerProvider;
+
     private CommandLine cmd;
     private StringWriter sout;
     private StringWriter serr;
@@ -53,7 +63,9 @@ public class CliCommandTestIntegrationBase extends 
IntegrationTestBase {
     @BeforeEach
     public void setUp(TestInfo testInfo) throws Exception {
         super.setUp(testInfo);
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createIntegrationTests());
         cmd = new CommandLine(getCommandClass(), new 
MicronautFactory(context));
+        cmd.setDefaultValueProvider(configDefaultValueProvider);
         sout = new StringWriter();
         serr = new StringWriter();
         cmd.setOut(new PrintWriter(sout));
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/Main.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/Main.java
index eeb0f37c9..dac94907d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/Main.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/Main.java
@@ -28,7 +28,7 @@ import java.util.HashMap;
 import java.util.stream.Collectors;
 import org.apache.ignite.cli.commands.TopLevelCliCommand;
 import org.apache.ignite.cli.commands.TopLevelCliReplCommand;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigDefaultValueProvider;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
 import org.apache.ignite.cli.core.call.StringCallInput;
 import org.apache.ignite.cli.core.exception.handler.DefaultExceptionHandlers;
@@ -113,8 +113,7 @@ public class Main {
     private static int executeCommand(String[] args, MicronautFactory 
micronautFactory) throws Exception {
         CommandLine cmd = new CommandLine(TopLevelCliCommand.class, 
micronautFactory);
         cmd.setExecutionExceptionHandler(new 
PicocliExecutionExceptionHandler());
-        Config config = micronautFactory.create(Config.class);
-        cmd.setDefaultValueProvider(new 
CommandLine.PropertiesDefaultProvider(config.getProperties()));
+        
cmd.setDefaultValueProvider(micronautFactory.create(ConfigDefaultValueProvider.class));
         return cmd.execute(args);
     }
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
index fb7a4810e..1d8748bf3 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
@@ -19,21 +19,25 @@ package org.apache.ignite.cli.call.cliconfig;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
+import org.apache.ignite.cli.config.Profile;
 import org.apache.ignite.cli.core.call.Call;
 import org.apache.ignite.cli.core.call.DefaultCallOutput;
-import org.apache.ignite.cli.core.call.EmptyCallInput;
+import org.apache.ignite.cli.core.call.StringCallInput;
 
 /**
  * Gets entire CLI configuration.
  */
 @Singleton
-public class CliConfigCall implements Call<EmptyCallInput, Config> {
+public class CliConfigCall implements Call<StringCallInput, Profile> {
     @Inject
-    private Config config;
+    private ConfigManagerProvider configManagerProvider;
 
     @Override
-    public DefaultCallOutput<Config> execute(EmptyCallInput input) {
-        return DefaultCallOutput.success(config);
+    public DefaultCallOutput<Profile> execute(StringCallInput input) {
+        ConfigManager configManager = configManagerProvider.get();
+        String profile = input.getString();
+        return DefaultCallOutput.success(profile == null ? 
configManager.getCurrentProfile() : configManager.getProfile(profile));
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
index 3b72ceb8e..65a80cfc0 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
@@ -19,22 +19,26 @@ package org.apache.ignite.cli.call.cliconfig;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
+import org.apache.ignite.cli.config.Profile;
 import org.apache.ignite.cli.core.call.Call;
 import org.apache.ignite.cli.core.call.DefaultCallOutput;
-import org.apache.ignite.cli.core.call.StringCallInput;
 
 /**
  * Gets CLI configuration parameter.
  */
 @Singleton
-public class CliConfigGetCall implements Call<StringCallInput, String> {
+public class CliConfigGetCall implements Call<CliConfigGetCallInput, String> {
     @Inject
-    private Config config;
+    private ConfigManagerProvider configManagerProvider;
 
     @Override
-    public DefaultCallOutput<String> execute(StringCallInput input) {
-        String key = input.getString();
+    public DefaultCallOutput<String> execute(CliConfigGetCallInput input) {
+        ConfigManager configManager = configManagerProvider.get();
+        String key = input.getKey();
+        String profileName = input.getProfileName();
+        Profile config = profileName == null ? 
configManager.getCurrentProfile() : configManager.getProfile(profileName);
         String property = config.getProperty(key, "");
         return DefaultCallOutput.success(property);
     }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCallInput.java
similarity index 69%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCallInput.java
index 9e8ac8683..eaccc8904 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCallInput.java
@@ -17,20 +17,26 @@
 
 package org.apache.ignite.cli.call.cliconfig;
 
-import java.util.Map;
 import org.apache.ignite.cli.core.call.CallInput;
 
 /**
- * Input for {@link CliConfigSetCall}.
+ * Get config property parameters.
  */
-public class CliConfigSetCallInput implements CallInput {
-    private final Map<String, String> parameters;
+public class CliConfigGetCallInput implements CallInput {
+    private final String key;
 
-    public CliConfigSetCallInput(Map<String, String> parameters) {
-        this.parameters = parameters;
+    private final String profileName;
+
+    public CliConfigGetCallInput(String key, String profileName) {
+        this.key = key;
+        this.profileName = profileName;
+    }
+
+    public String getKey() {
+        return key;
     }
 
-    public Map<String, String> getParameters() {
-        return parameters;
+    public String getProfileName() {
+        return profileName;
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
index b2c5a3647..5bbf6623e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
@@ -19,8 +19,9 @@ package org.apache.ignite.cli.call.cliconfig;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import java.util.Map.Entry;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
+import org.apache.ignite.cli.config.Profile;
 import org.apache.ignite.cli.core.call.Call;
 import org.apache.ignite.cli.core.call.DefaultCallOutput;
 
@@ -30,15 +31,14 @@ import org.apache.ignite.cli.core.call.DefaultCallOutput;
 @Singleton
 public class CliConfigSetCall implements Call<CliConfigSetCallInput, Object> {
     @Inject
-    private Config config;
+    private ConfigManagerProvider configManagerProvider;
 
     @Override
     public DefaultCallOutput<Object> execute(CliConfigSetCallInput input) {
-        for (Entry<String, String> entry : input.getParameters().entrySet()) {
-            config.setProperty(entry.getKey(), entry.getValue());
-        }
-        config.saveConfig();
-
+        ConfigManager configManager = configManagerProvider.get();
+        String profileName = input.getProfileName();
+        Profile config = profileName == null ? 
configManager.getCurrentProfile() : configManager.getProfile(profileName);
+        config.setProperties(input.getParameters());
         return DefaultCallOutput.empty();
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
index 9e8ac8683..4bc0ee1dd 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
@@ -26,11 +26,18 @@ import org.apache.ignite.cli.core.call.CallInput;
 public class CliConfigSetCallInput implements CallInput {
     private final Map<String, String> parameters;
 
-    public CliConfigSetCallInput(Map<String, String> parameters) {
+    private final String profileName;
+
+    public CliConfigSetCallInput(Map<String, String> parameters, String 
profileName) {
         this.parameters = parameters;
+        this.profileName = profileName;
     }
 
     public Map<String, String> getParameters() {
         return parameters;
     }
+
+    public String getProfileName() {
+        return profileName;
+    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigCreateProfileCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigCreateProfileCall.java
new file mode 100644
index 000000000..685fc4f5c
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigCreateProfileCall.java
@@ -0,0 +1,58 @@
+/*
+ * 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.ignite.cli.call.cliconfig.profile;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
+import org.apache.ignite.cli.config.Profile;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+
+/**
+ * Create CLI profile.
+ */
+@Singleton
+public class CliConfigCreateProfileCall implements 
Call<CliConfigCreateProfileCallInput, String> {
+
+    @Inject
+    private ConfigManagerProvider configManagerProvider;
+
+    @Override
+    public CallOutput<String> execute(CliConfigCreateProfileCallInput input) {
+        ConfigManager configManager = configManagerProvider.get();
+        Profile copyFrom = null;
+        if (input.getCopyFrom() != null) {
+            copyFrom = configManager.getProfile(input.getCopyFrom());
+        }
+
+        String profileName = input.getName();
+        Profile newProfile = configManager.createProfile(profileName);
+
+        if (copyFrom != null) {
+            newProfile.setProperties(copyFrom);
+        }
+
+        if (input.isActivate()) {
+            configManager.setCurrentProfile(profileName);
+        }
+        return DefaultCallOutput.success("Profile " + profileName + " was 
created successfully.");
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigCreateProfileCallInput.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigCreateProfileCallInput.java
new file mode 100644
index 000000000..f61ef7bf3
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigCreateProfileCallInput.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cli.call.cliconfig.profile;
+
+import org.apache.ignite.cli.core.call.CallInput;
+
+/**
+ * Create profile call parameters.
+ */
+public class CliConfigCreateProfileCallInput implements CallInput {
+    private final String name;
+
+    private final String copyFrom;
+
+    private final boolean activate;
+
+    private CliConfigCreateProfileCallInput(String name, String copyFrom, 
boolean activate) {
+        this.name = name;
+        this.copyFrom = copyFrom;
+        this.activate = activate;
+    }
+
+    public static CliConfigCreateProfileCallInputBuilder builder() {
+        return new CliConfigCreateProfileCallInputBuilder();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getCopyFrom() {
+        return copyFrom;
+    }
+
+    public boolean isActivate() {
+        return activate;
+    }
+
+    /**
+     * Builder of {@link CliConfigCreateProfileCallInput}.
+     */
+    public static class CliConfigCreateProfileCallInputBuilder {
+        private String name;
+
+        private String copyFrom;
+
+        private boolean activate;
+
+        public CliConfigCreateProfileCallInputBuilder profileName(String name) 
{
+            this.name = name;
+            return this;
+        }
+
+        public CliConfigCreateProfileCallInputBuilder copyFrom(String 
copyFrom) {
+            this.copyFrom = copyFrom;
+            return this;
+        }
+
+        public CliConfigCreateProfileCallInputBuilder activate(boolean 
activate) {
+            this.activate = activate;
+            return this;
+        }
+
+        public CliConfigCreateProfileCallInput build() {
+            return new CliConfigCreateProfileCallInput(name, copyFrom, 
activate);
+        }
+    }
+
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigShowProfileCall.java
similarity index 67%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigShowProfileCall.java
index fb7a4810e..c9115b0c3 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigShowProfileCall.java
@@ -15,25 +15,28 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.call.cliconfig;
+package org.apache.ignite.cli.call.cliconfig.profile;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
 import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
 import org.apache.ignite.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.cli.core.call.EmptyCallInput;
 
 /**
- * Gets entire CLI configuration.
+ * Get current profile call.
  */
 @Singleton
-public class CliConfigCall implements Call<EmptyCallInput, Config> {
+public class CliConfigShowProfileCall implements Call<EmptyCallInput, String> {
+
     @Inject
-    private Config config;
+    private ConfigManagerProvider provider;
 
     @Override
-    public DefaultCallOutput<Config> execute(EmptyCallInput input) {
-        return DefaultCallOutput.success(config);
+    public CallOutput<String> execute(EmptyCallInput input) {
+        String profileName = provider.get().getCurrentProfile().getName();
+        return DefaultCallOutput.success("Current profile: " + profileName);
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigUseCall.java
similarity index 65%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigUseCall.java
index 3b72ceb8e..670fba3f0 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/profile/CliConfigUseCall.java
@@ -15,27 +15,28 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.call.cliconfig;
+package org.apache.ignite.cli.call.cliconfig.profile;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
 import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
 import org.apache.ignite.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.cli.core.call.StringCallInput;
 
 /**
- * Gets CLI configuration parameter.
+ * Activate CLI profile as default.
  */
 @Singleton
-public class CliConfigGetCall implements Call<StringCallInput, String> {
+public class CliConfigUseCall implements Call<StringCallInput, String> {
+
     @Inject
-    private Config config;
+    private ConfigManagerProvider configManagerProvider;
 
     @Override
-    public DefaultCallOutput<String> execute(StringCallInput input) {
-        String key = input.getString();
-        String property = config.getProperty(key, "");
-        return DefaultCallOutput.success(property);
+    public CallOutput<String> execute(StringCallInput input) {
+        configManagerProvider.get().setCurrentProfile(input.getString());
+        return DefaultCallOutput.success("Profile " + input.getString() + " 
was activated successfully.");
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
index 9ad7a23b2..46d9388ce 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
@@ -20,10 +20,11 @@ package org.apache.ignite.cli.commands.cliconfig;
 import jakarta.inject.Inject;
 import java.util.concurrent.Callable;
 import org.apache.ignite.cli.call.cliconfig.CliConfigGetCall;
+import org.apache.ignite.cli.call.cliconfig.CliConfigGetCallInput;
 import org.apache.ignite.cli.commands.BaseCommand;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
-import org.apache.ignite.cli.core.call.StringCallInput;
 import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 import picocli.CommandLine.Parameters;
 
 /**
@@ -34,13 +35,16 @@ public class CliConfigGetSubCommand extends BaseCommand 
implements Callable<Inte
     @Parameters
     private String key;
 
+    @Option(names = {"--profile", "-p"}, description = "Get property from 
specified profile.")
+    private String profileName;
+
     @Inject
     private CliConfigGetCall call;
 
     @Override
     public Integer call() {
         return CallExecutionPipeline.builder(call)
-                .inputProvider(() -> new StringCallInput(key))
+                .inputProvider(() -> new CliConfigGetCallInput(key, 
profileName))
                 .output(spec.commandLine().getOut())
                 .errOutput(spec.commandLine().getErr())
                 .build()
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
index f78b82d78..1bfc8bba6 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
@@ -25,6 +25,7 @@ import 
org.apache.ignite.cli.call.cliconfig.CliConfigSetCallInput;
 import org.apache.ignite.cli.commands.BaseCommand;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
 import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 import picocli.CommandLine.Parameters;
 
 /**
@@ -35,13 +36,16 @@ public class CliConfigSetSubCommand extends BaseCommand 
implements Callable<Inte
     @Parameters(arity = "1..*")
     private Map<String, String> parameters;
 
+    @Option(names = {"--profile", "-p"}, description = "Set property in 
specified profile.")
+    private String profileName;
+
     @Inject
     private CliConfigSetCall call;
 
     @Override
     public Integer call() {
         return CallExecutionPipeline.builder(call)
-                .inputProvider(() -> new CliConfigSetCallInput(parameters))
+                .inputProvider(() -> new CliConfigSetCallInput(parameters, 
profileName))
                 .output(spec.commandLine().getOut())
                 .errOutput(spec.commandLine().getErr())
                 .build()
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
index a622631db..6e0d2f7cc 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
@@ -22,28 +22,34 @@ import jakarta.inject.Singleton;
 import java.util.concurrent.Callable;
 import org.apache.ignite.cli.call.cliconfig.CliConfigCall;
 import org.apache.ignite.cli.commands.BaseCommand;
+import 
org.apache.ignite.cli.commands.cliconfig.profile.CliConfigProfileCommand;
 import org.apache.ignite.cli.commands.decorators.ConfigDecorator;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
-import org.apache.ignite.cli.core.call.EmptyCallInput;
+import org.apache.ignite.cli.core.call.StringCallInput;
 import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 /**
  * Parent command for CLI configuration commands.
  */
 @Command(name = "config", subcommands = {
         CliConfigGetSubCommand.class,
-        CliConfigSetSubCommand.class
+        CliConfigSetSubCommand.class,
+        CliConfigProfileCommand.class,
 })
 @Singleton
 public class CliConfigSubCommand extends BaseCommand implements 
Callable<Integer> {
 
+    @Option(names = {"--profile", "-p"})
+    private String profileName;
+
     @Inject
     private CliConfigCall call;
 
     @Override
     public Integer call() {
         return CallExecutionPipeline.builder(call)
-                .inputProvider(EmptyCallInput::new)
+                .inputProvider(() -> new StringCallInput(profileName))
                 .output(spec.commandLine().getOut())
                 .errOutput(spec.commandLine().getErr())
                 .decorator(new ConfigDecorator())
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java
similarity index 52%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java
index f78b82d78..848e9cfc1 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java
@@ -15,36 +15,44 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.commands.cliconfig;
+package org.apache.ignite.cli.commands.cliconfig.profile;
 
 import jakarta.inject.Inject;
-import java.util.Map;
 import java.util.concurrent.Callable;
-import org.apache.ignite.cli.call.cliconfig.CliConfigSetCall;
-import org.apache.ignite.cli.call.cliconfig.CliConfigSetCallInput;
+import org.apache.ignite.cli.call.cliconfig.profile.CliConfigCreateProfileCall;
+import 
org.apache.ignite.cli.call.cliconfig.profile.CliConfigCreateProfileCallInput;
 import org.apache.ignite.cli.commands.BaseCommand;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
 import picocli.CommandLine.Command;
-import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Option;
 
 /**
- * Command to set CLI configuration parameters.
+ * Command for create CLI profile.
  */
-@Command(name = "set")
-public class CliConfigSetSubCommand extends BaseCommand implements 
Callable<Integer> {
-    @Parameters(arity = "1..*")
-    private Map<String, String> parameters;
+@Command(name = "create", description = "Create profile command.")
+public class CliConfigCreateProfileCommand extends BaseCommand implements 
Callable<Integer> {
+    @Option(names = {"--name", "-n"}, required = true, description = "Name of 
new profile.")
+    private String profileName;
+
+    @Option(names = {"--copy-from", "-c"}, description = "Profile whose 
content will be copied to new one.")
+    private String copyFrom;
+
+    @Option(names = {"--activate", "-a"}, description = "Activate new profile 
as current or not.")
+    private boolean activate;
+
 
     @Inject
-    private CliConfigSetCall call;
+    private CliConfigCreateProfileCall call;
 
     @Override
     public Integer call() {
         return CallExecutionPipeline.builder(call)
-                .inputProvider(() -> new CliConfigSetCallInput(parameters))
-                .output(spec.commandLine().getOut())
+                .inputProvider(CliConfigCreateProfileCallInput.builder()
+                        .profileName(profileName)
+                        .copyFrom(copyFrom)
+                        .activate(activate)::build)
                 .errOutput(spec.commandLine().getErr())
-                .build()
-                .runPipeline();
+                .output(spec.commandLine().getOut())
+                .build().runPipeline();
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java
similarity index 62%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java
index 9ad7a23b2..dd83cd92e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java
@@ -15,35 +15,39 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.commands.cliconfig;
+package org.apache.ignite.cli.commands.cliconfig.profile;
 
 import jakarta.inject.Inject;
 import java.util.concurrent.Callable;
-import org.apache.ignite.cli.call.cliconfig.CliConfigGetCall;
+import org.apache.ignite.cli.call.cliconfig.profile.CliConfigUseCall;
 import org.apache.ignite.cli.commands.BaseCommand;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
 import org.apache.ignite.cli.core.call.StringCallInput;
 import picocli.CommandLine.Command;
-import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Option;
 
 /**
- * Command to get CLI configuration parameters.
+ * Root profile command.
  */
-@Command(name = "get")
-public class CliConfigGetSubCommand extends BaseCommand implements 
Callable<Integer> {
-    @Parameters
-    private String key;
+@Command(name = "profile",
+        description = "Create profile command.",
+        subcommands = {
+                CliConfigCreateProfileCommand.class,
+                CliConfigShowProfileCommand.class
+        })
+public class CliConfigProfileCommand extends BaseCommand implements 
Callable<Integer> {
+    @Option(names = {"--set-current", "-s"}, description = "Name of profile 
which should be activated as default.")
+    private String profileName;
 
     @Inject
-    private CliConfigGetCall call;
+    private CliConfigUseCall call;
 
     @Override
     public Integer call() {
         return CallExecutionPipeline.builder(call)
-                .inputProvider(() -> new StringCallInput(key))
-                .output(spec.commandLine().getOut())
+                .inputProvider(() -> new StringCallInput(profileName))
                 .errOutput(spec.commandLine().getErr())
-                .build()
-                .runPipeline();
+                .output(spec.commandLine().getOut())
+                .build().runPipeline();
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java
similarity index 68%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java
index 9ad7a23b2..ca300b7e3 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java
@@ -15,32 +15,29 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.commands.cliconfig;
+package org.apache.ignite.cli.commands.cliconfig.profile;
 
 import jakarta.inject.Inject;
 import java.util.concurrent.Callable;
-import org.apache.ignite.cli.call.cliconfig.CliConfigGetCall;
+import org.apache.ignite.cli.call.cliconfig.profile.CliConfigShowProfileCall;
 import org.apache.ignite.cli.commands.BaseCommand;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
-import org.apache.ignite.cli.core.call.StringCallInput;
-import picocli.CommandLine.Command;
-import picocli.CommandLine.Parameters;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
+import picocli.CommandLine;
 
 /**
- * Command to get CLI configuration parameters.
+ * Show current profile command.
  */
-@Command(name = "get")
-public class CliConfigGetSubCommand extends BaseCommand implements 
Callable<Integer> {
-    @Parameters
-    private String key;
+@CommandLine.Command(name = "show", description = "Show current default 
profile.")
+public class CliConfigShowProfileCommand extends BaseCommand implements 
Callable<Integer> {
 
     @Inject
-    private CliConfigGetCall call;
+    private CliConfigShowProfileCall call;
 
     @Override
-    public Integer call() {
+    public Integer call() throws Exception {
         return CallExecutionPipeline.builder(call)
-                .inputProvider(() -> new StringCallInput(key))
+                .inputProvider(EmptyCallInput::new)
                 .output(spec.commandLine().getOut())
                 .errOutput(spec.commandLine().getErr())
                 .build()
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
index d73a19a49..056cbd400 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
@@ -22,6 +22,7 @@ import jakarta.inject.Singleton;
 import org.apache.ignite.cli.call.connect.ConnectCall;
 import org.apache.ignite.cli.call.connect.ConnectCallInput;
 import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.config.ConfigConstants;
 import org.apache.ignite.cli.core.call.CallExecutionPipeline;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Parameters;
@@ -38,7 +39,7 @@ public class ConnectCommand extends BaseCommand implements 
Runnable {
      */
     @Parameters(
             description = "Ignite node url.",
-            descriptionKey = "ignite.cluster-url", defaultValue = 
"http://localhost:10300";
+            descriptionKey = ConfigConstants.CLUSTER_URL
     )
     private String nodeUrl;
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java
index 87c91adf5..3032a073b 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java
@@ -17,26 +17,19 @@
 
 package org.apache.ignite.cli.commands.decorators;
 
-import java.util.Iterator;
-import java.util.Map.Entry;
+import java.util.stream.Collectors;
 import org.apache.ignite.cli.commands.decorators.core.Decorator;
 import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.Profile;
 
 /**
- * Decorator for printing {@link Config}.
+ * Decorator for printing {@link Profile}.
  */
-public class ConfigDecorator implements Decorator<Config, TerminalOutput> {
+public class ConfigDecorator implements Decorator<Profile, TerminalOutput> {
     @Override
-    public TerminalOutput decorate(Config data) {
-        StringBuilder builder = new StringBuilder();
-        for (Iterator<Entry<Object, Object>> iterator = 
data.getProperties().entrySet().iterator(); iterator.hasNext(); ) {
-            Entry<Object, Object> entry = iterator.next();
-            
builder.append(entry.getKey()).append("=").append(entry.getValue());
-            if (iterator.hasNext()) {
-                builder.append(System.lineSeparator());
-            }
-        }
-        return builder::toString;
+    public TerminalOutput decorate(Profile data) {
+        return () -> data.getAll().entrySet().stream()
+                .map(entry -> entry.getKey() + "=" + entry.getValue())
+                .collect(Collectors.joining(System.lineSeparator()));
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/CachedConfigManagerProvider.java
similarity index 70%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/CachedConfigManagerProvider.java
index a8e09d2f3..3c364d3c1 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/CachedConfigManagerProvider.java
@@ -17,16 +17,18 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
 import jakarta.inject.Singleton;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
 
 /**
- * Factory for {@link Config}.
+ * Provider for {@link ConfigManager}.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+@Singleton
+public class CachedConfigManagerProvider implements ConfigManagerProvider {
+    private final ConfigManager configManager = new 
IniConfigManager(ConfigConstants.getConfigFile());
+
+    @Override
+    public ConfigManager get() {
+        return configManager;
     }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/config/Config.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/Config.java
deleted file mode 100644
index 04374689b..000000000
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/Config.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      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.ignite.cli.config;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.util.Properties;
-
-/**
- * CLI default configuration.
- */
-public class Config {
-    private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
-    private static final String XDG_STATE_HOME = "XDG_STATE_HOME";
-    private static final String PARENT_FOLDER_NAME = "ignitecli";
-    private static final String CONFIG_FILE_NAME = "defaults";
-
-    private final File configFile;
-    private final Properties props = new Properties();
-
-    public Config(File configFile) {
-        this.configFile = configFile;
-        loadConfig(configFile);
-    }
-
-    /**
-     * Loads config from the default location specified by the XDG_CONFIG_HOME.
-     */
-    public Config() {
-        this(getConfigFile());
-    }
-
-    public Properties getProperties() {
-        return props;
-    }
-
-    public String getProperty(String key) {
-        return props.getProperty(key);
-    }
-
-    public String getProperty(String key, String defaultValue) {
-        return props.getProperty(key, defaultValue);
-    }
-
-    public void setProperty(String key, String value) {
-        props.setProperty(key, value);
-    }
-
-    private void loadConfig(File configFile) {
-        if (configFile.canRead()) {
-            try (InputStream is = new FileInputStream(configFile)) {
-                props.load(is);
-            } catch (IOException e) {
-                // todo report error?
-            }
-        }
-    }
-
-    /**
-     * Saves config to file.
-     */
-    public void saveConfig() {
-        configFile.getParentFile().mkdirs();
-        if (configFile.canWrite()) {
-            try (OutputStream os = new FileOutputStream(configFile)) {
-                props.store(os, null);
-            } catch (IOException e) {
-                // todo report error?
-            }
-        }
-    }
-
-    private static File getConfigFile() {
-        return 
getConfigRoot().resolve(PARENT_FOLDER_NAME).resolve(CONFIG_FILE_NAME).toFile();
-    }
-
-    /**
-     * Gets the path for the state.
-     *
-     * @return Folder for state storage.
-     */
-    public static File getStateFolder() {
-        return getStateRoot().resolve(PARENT_FOLDER_NAME).toFile();
-    }
-
-    private static Path getConfigRoot() {
-        String xdgConfigHome = System.getenv(XDG_CONFIG_HOME);
-        if (xdgConfigHome != null) {
-            return Path.of(xdgConfigHome);
-        } else {
-            return Path.of(System.getProperty("user.home"), ".config");
-        }
-    }
-
-    private static Path getStateRoot() {
-        String xdgStateHome = System.getenv(XDG_STATE_HOME);
-        if (xdgStateHome != null) {
-            return Path.of(xdgStateHome);
-        } else {
-            return Path.of(System.getProperty("user.home"), ".local", "state");
-        }
-    }
-}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigConstants.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigConstants.java
new file mode 100644
index 000000000..fcc2f9864
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigConstants.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ignite.cli.config;
+
+import java.io.File;
+import java.nio.file.Path;
+
+/**
+ * Util class for config CLI.
+ */
+public final class ConfigConstants {
+    private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
+    private static final String PARENT_FOLDER_NAME = "ignitecli";
+    private static final String CONFIG_FILE_NAME = "defaults";
+
+    public static final String CURRENT_PROFILE = "current_profile";
+    public static final String CLUSTER_URL = "ignite.cluster-url";
+    public static final String JDBC_URL = "ignite.jdbc-url";
+
+    private ConfigConstants() {
+
+    }
+
+
+    public static File getConfigFile() {
+        return 
getConfigRoot().resolve(PARENT_FOLDER_NAME).resolve(CONFIG_FILE_NAME).toFile();
+    }
+
+    private static Path getConfigRoot() {
+        String xdgConfigHome = System.getenv(XDG_CONFIG_HOME);
+        if (xdgConfigHome != null) {
+            return Path.of(xdgConfigHome);
+        } else {
+            return Path.of(System.getProperty("user.home"), ".config");
+        }
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigDefaultValueProvider.java
similarity index 65%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigDefaultValueProvider.java
index fb7a4810e..37e2fefd0 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigDefaultValueProvider.java
@@ -15,25 +15,22 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.call.cliconfig;
+package org.apache.ignite.cli.config;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import org.apache.ignite.cli.config.Config;
-import org.apache.ignite.cli.core.call.Call;
-import org.apache.ignite.cli.core.call.DefaultCallOutput;
-import org.apache.ignite.cli.core.call.EmptyCallInput;
+import picocli.CommandLine;
 
 /**
- * Gets entire CLI configuration.
+ * Implementation of {@link CommandLine.IDefaultValueProvider} based on CLI 
config API.
  */
 @Singleton
-public class CliConfigCall implements Call<EmptyCallInput, Config> {
+public class ConfigDefaultValueProvider implements 
CommandLine.IDefaultValueProvider {
     @Inject
-    private Config config;
+    private ConfigManagerProvider configManagerProvider;
 
     @Override
-    public DefaultCallOutput<Config> execute(EmptyCallInput input) {
-        return DefaultCallOutput.success(config);
+    public String defaultValue(CommandLine.Model.ArgSpec argSpec) throws 
Exception {
+        return 
configManagerProvider.get().getCurrentProperty(argSpec.descriptionKey());
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigInitializationException.java
similarity index 68%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigInitializationException.java
index a8e09d2f3..b8a95af4b 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigInitializationException.java
@@ -17,16 +17,18 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-
 /**
- * Factory for {@link Config}.
+ * Config initialization exception.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+public class ConfigInitializationException extends RuntimeException {
+
+    /**
+     * Constructor.
+     *
+     * @param configPath path of not initialized config.
+     * @param e reason.
+     */
+    public ConfigInitializationException(String configPath, Throwable e) {
+        super("Failed to initialize default config in location: " + 
configPath, e);
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigManager.java
similarity index 65%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigManager.java
index a8e09d2f3..681e81ffa 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigManager.java
@@ -17,16 +17,23 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-
 /**
- * Factory for {@link Config}.
+ * Manager of CLI config.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+public interface ConfigManager {
+    Profile getCurrentProfile();
+
+    Profile getProfile(String profile);
+
+    Profile createProfile(String profileName);
+
+    void setCurrentProfile(String profileName);
+
+    default String getCurrentProperty(String key) {
+        return getCurrentProfile().getProperty(key);
+    }
+
+    default void setProperty(String key, String value) {
+        getCurrentProfile().setProperty(key, value);
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigManagerProvider.java
similarity index 78%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigManagerProvider.java
index a8e09d2f3..921e65138 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigManagerProvider.java
@@ -17,16 +17,9 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-
 /**
- * Factory for {@link Config}.
+ * Provider for {@link ConfigManager}.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
-    }
+public interface ConfigManagerProvider {
+    ConfigManager get();
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigStoringException.java
similarity index 74%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigStoringException.java
index a8e09d2f3..f7149dc49 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigStoringException.java
@@ -17,16 +17,17 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-
 /**
- * Factory for {@link Config}.
+ * Exception for case when config not stored correctly.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+public class ConfigStoringException extends RuntimeException {
+
+    /**
+     * Constructor.
+     *
+     * @param cause cause exception.
+     */
+    public ConfigStoringException(String message, Throwable cause) {
+        super(message, cause);
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/Profile.java
similarity index 69%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/config/Profile.java
index a8e09d2f3..31d47a5fb 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/Profile.java
@@ -17,16 +17,23 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
+import java.util.Map;
 
 /**
- * Factory for {@link Config}.
+ * Ignite CLI Profile.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
-    }
+public interface Profile {
+    String getName();
+
+    Map<String, String> getAll();
+
+    String getProperty(String key);
+
+    String getProperty(String key, String defaultValue);
+
+    void setProperty(String key, String value);
+
+    void setProperties(Map<String, String> values);
+
+    void setProperties(Profile copyFrom);
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ProfileNotFoundException.java
similarity index 79%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ProfileNotFoundException.java
index a8e09d2f3..a1e0b671d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ProfileNotFoundException.java
@@ -17,16 +17,11 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-
 /**
- * Factory for {@link Config}.
+ * CLI profile not found.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+public class ProfileNotFoundException extends RuntimeException {
+    public ProfileNotFoundException(String profileName) {
+        super("Profile " + profileName + " not found.");
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/StateFolderProvider.java
similarity index 51%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/config/StateFolderProvider.java
index a8e09d2f3..021cd93b5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/StateFolderProvider.java
@@ -17,16 +17,35 @@
 
 package org.apache.ignite.cli.config;
 
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
+import java.io.File;
+import java.nio.file.Path;
 
 /**
- * Factory for {@link Config}.
+ * Helper class to access to state folder of CLI.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+public final class StateFolderProvider {
+    private static final String XDG_STATE_HOME = "XDG_STATE_HOME";
+    private static final String PARENT_FOLDER_NAME = "ignitecli";
+
+    private StateFolderProvider() {
+
+    }
+
+    /**
+     * Gets the path for the state.
+     *
+     * @return Folder for state storage.
+     */
+    public static File getStateFolder() {
+        return getStateRoot().resolve(PARENT_FOLDER_NAME).toFile();
+    }
+
+    private static Path getStateRoot() {
+        String xdgStateHome = System.getenv(XDG_STATE_HOME);
+        if (xdgStateHome != null) {
+            return Path.of(xdgStateHome);
+        } else {
+            return Path.of(System.getProperty("user.home"), ".local", "state");
+        }
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniConfigManager.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniConfigManager.java
new file mode 100644
index 000000000..bba9f1739
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniConfigManager.java
@@ -0,0 +1,123 @@
+/*
+ * 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.ignite.cli.config.ini;
+
+import static org.apache.ignite.cli.config.ConfigConstants.CLUSTER_URL;
+import static org.apache.ignite.cli.config.ConfigConstants.CURRENT_PROFILE;
+import static org.apache.ignite.cli.config.ConfigConstants.JDBC_URL;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.NoSuchElementException;
+import org.apache.ignite.cli.config.ConfigConstants;
+import org.apache.ignite.cli.config.ConfigInitializationException;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.Profile;
+import org.apache.ignite.cli.config.ProfileNotFoundException;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+
+/**
+ * Implementation of {@link ConfigManager} based on {@link IniFile}.
+ */
+public class IniConfigManager implements ConfigManager {
+    private static final IgniteLogger log = 
Loggers.forClass(IniConfigManager.class);
+
+    private static final String INTERNAL_SECTION_NAME = "ignitecli_internal";
+
+    private final IniFile configFile;
+
+    private String currentProfileName;
+
+    /**
+     * Constructor.
+     *
+     * @param file ini file.
+     */
+    public IniConfigManager(File file) {
+        IniFile configFile;
+        try {
+            configFile = new IniFile(file);
+            findCurrentProfile(configFile);
+        } catch (IOException | NoSuchElementException e) {
+            log.warn("User config is corrupted or doesn't exist.", e);
+            configFile = createDefaultConfig();
+        }
+        this.configFile = configFile;
+        this.currentProfileName = 
findCurrentProfile(configFile).getProperty(CURRENT_PROFILE);
+    }
+
+    private static IniSection findCurrentProfile(IniFile configFile) {
+        IniSection internalSection = 
configFile.getSection(INTERNAL_SECTION_NAME);
+        if (internalSection == null) {
+            IniSection section = 
configFile.getSections().stream().findFirst().orElseThrow();
+            internalSection = configFile.createSection(INTERNAL_SECTION_NAME);
+            internalSection.setProperty(CURRENT_PROFILE, section.getName());
+        }
+        return internalSection;
+    }
+
+    @Override
+    public Profile getCurrentProfile() {
+        return getProfile(currentProfileName);
+    }
+
+    @Override
+    public Profile getProfile(String profile) {
+        IniSection section = configFile.getSection(profile);
+        if (section == null) {
+            throw new ProfileNotFoundException(profile);
+        }
+        return new IniProfile(section, configFile::store);
+    }
+
+    @Override
+    public Profile createProfile(String profileName) {
+        return new IniProfile(configFile.createSection(profileName), 
configFile::store);
+    }
+
+    @Override
+    public void setCurrentProfile(String profileName) {
+        IniSection section = configFile.getSection(profileName);
+        if (section == null) {
+            throw new ProfileNotFoundException(profileName);
+        }
+        currentProfileName = profileName;
+        
configFile.getSection(INTERNAL_SECTION_NAME).setProperty(CURRENT_PROFILE, 
profileName);
+        configFile.store();
+    }
+
+    private static IniFile createDefaultConfig() {
+        File configFile = ConfigConstants.getConfigFile();
+        try {
+            configFile.getParentFile().mkdirs();
+            configFile.delete();
+            configFile.createNewFile();
+            IniFile ini = new IniFile(configFile);
+            IniSection internal = ini.createSection(INTERNAL_SECTION_NAME);
+            internal.setProperty("current_profile", "default");
+            IniSection defaultSection = ini.createSection("default");
+            defaultSection.setProperty(CLUSTER_URL, "http://localhost:10300";);
+            defaultSection.setProperty(JDBC_URL, 
"jdbc:ignite:thin://127.0.0.1:10800");
+            ini.store();
+            return ini;
+        } catch (IOException e) {
+            throw new 
ConfigInitializationException(configFile.getAbsolutePath(), e);
+        }
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniFile.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniFile.java
new file mode 100644
index 000000000..6dce2340b
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniFile.java
@@ -0,0 +1,98 @@
+/*
+ * 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.ignite.cli.config.ini;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.ignite.cli.config.ConfigStoringException;
+
+/**
+ * Representation of INI file.
+ */
+public class IniFile {
+    private final Map<String, IniSection> content = new LinkedHashMap<>();
+    private final File file;
+
+    /**
+     * Constructor.
+     *
+     * @param file ini file.
+     * @throws IOException in case when provided INI file can't be parsed.
+     */
+    public IniFile(File file) throws IOException {
+        content.putAll(new IniParser().parse(file));
+        this.file = file;
+    }
+
+    public IniSection getSection(String name) {
+        return content.get(name);
+    }
+
+    public Collection<IniSection> getSections() {
+        return content.values();
+    }
+
+    /**
+     * Store current INI file to FS file.
+     */
+    public void store() {
+        try (OutputStream os = new FileOutputStream(file)) {
+            store(os);
+        } catch (IOException e) {
+            throw new ConfigStoringException("Can't store cli config file " + 
file.getAbsolutePath(), e);
+        }
+    }
+
+    private void store(OutputStream outputStream) throws IOException {
+        BufferedWriter bufferedWriter = new BufferedWriter(new 
OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
+        for (IniSection section : getSections()) {
+            bufferedWriter.write("[" + section.getName() + "]");
+            bufferedWriter.newLine();
+            for (Map.Entry<String, String> sectionEntry : 
section.getAll().entrySet()) {
+                bufferedWriter.write(sectionEntry.getKey() + " = ");
+                bufferedWriter.write(sectionEntry.getValue());
+                bufferedWriter.newLine();
+            }
+            bufferedWriter.newLine();
+        }
+        bufferedWriter.flush();
+    }
+
+    /**
+     * Create and return new {@link IniSection} with provided name.
+     *
+     * @param name of section.
+     * @return new section.
+     */
+    public IniSection createSection(String name) {
+        if (content.containsKey(name)) {
+            throw new SectionAlreadyExistsException(name);
+        }
+        IniSection iniSection = new IniSection(name);
+        content.put(name, iniSection);
+        return iniSection;
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniParser.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniParser.java
new file mode 100644
index 000000000..c0eb2bb92
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniParser.java
@@ -0,0 +1,99 @@
+/*
+ * 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.ignite.cli.config.ini;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * INI file parser.
+ */
+public class IniParser {
+    private static final Pattern SECTION_PATTERN  = 
Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
+    private static final Pattern KEY_VALUE_PATTER = 
Pattern.compile("\\s*([^=]*)=(.*)");
+    private static final Pattern COMMENT_LINE = Pattern.compile("^[;|#].*");
+
+
+    /**
+     * Parse incoming input string as {@link Map}.
+     *
+     * @param inputStream of INI file.
+     * @return Map representation of INI file.
+     * @throws IOException when parsing failed.
+     */
+    public Map<String, IniSection> parse(InputStream inputStream) throws 
IOException {
+        if (inputStream == null) {
+            throw new FileNotFoundException("inputStream is null");
+        }
+
+        try (BufferedReader bufferedReader = new BufferedReader(new 
InputStreamReader(inputStream))) {
+            return parseIniFile(bufferedReader);
+        }
+    }
+
+    /**
+     * Parse incoming file as {@link Map}.
+     *
+     * @param file INI file.
+     * @return Map representation of INI file.
+     * @throws IOException when parsing failed.
+     */
+    public Map<String, IniSection> parse(File file) throws IOException {
+        try (FileInputStream input = new FileInputStream(file)) {
+            return parse(input);
+        }
+    }
+
+    private Map<String, IniSection> parseIniFile(BufferedReader 
bufferedReader) throws IOException {
+        Map<String, IniSection> map = new LinkedHashMap<>();
+        IniSection currentSection = new IniSection("NO_SECTION");
+        String line;
+        while ((line = bufferedReader.readLine()) != null) {
+
+            Matcher commentMatcher = COMMENT_LINE.matcher(line);
+            if (commentMatcher.matches()) {
+                continue;
+            }
+
+            Matcher sectionMather = SECTION_PATTERN.matcher(line);
+            if (sectionMather.matches()) {
+                String sectionName = sectionMather.group(1).trim();
+                currentSection = new IniSection(sectionName);
+                map.put(sectionName, currentSection);
+                continue;
+            }
+
+            Matcher keyValueMatcher = KEY_VALUE_PATTER.matcher(line);
+            if (keyValueMatcher.matches()) {
+                String key = keyValueMatcher.group(1).trim();
+                String value = keyValueMatcher.group(2).trim();
+                currentSection.setProperty(key, value);
+            }
+        }
+        return map;
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniProfile.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniProfile.java
new file mode 100644
index 000000000..dcda97307
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniProfile.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cli.config.ini;
+
+import java.util.Map;
+import org.apache.ignite.cli.config.Profile;
+
+/**
+ * Implementation of {@link Profile} based on {@link IniSection}.
+ */
+public class IniProfile implements Profile {
+    private final IniSection section;
+    private final Runnable saveAction;
+
+    public IniProfile(IniSection section, Runnable saveAction) {
+        this.section = section;
+        this.saveAction = saveAction;
+    }
+
+    @Override
+    public String getName() {
+        return section.getName();
+    }
+
+    @Override
+    public Map<String, String> getAll() {
+        return section.getAll();
+    }
+
+    @Override
+    public String getProperty(String key) {
+        return section.getProperty(key);
+    }
+
+    @Override
+    public String getProperty(String key, String defaultValue) {
+        return section.getProperty(key, defaultValue);
+    }
+
+    @Override
+    public void setProperty(String key, String value) {
+        section.setProperty(key, value);
+        saveAction.run();
+    }
+
+    @Override
+    public void setProperties(Map<String, String> values) {
+        section.setProperties(values);
+        saveAction.run();
+    }
+
+    @Override
+    public void setProperties(Profile copyFrom) {
+        section.setProperties(copyFrom);
+        saveAction.run();
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniSection.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniSection.java
new file mode 100644
index 000000000..eb6d95b70
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/IniSection.java
@@ -0,0 +1,63 @@
+/*
+ * 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.ignite.cli.config.ini;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.ignite.cli.config.Profile;
+
+/**
+ * INI section representation.
+ */
+public class IniSection {
+    private final String name;
+    private final Map<String, String> props = new LinkedHashMap<>();
+
+    public IniSection(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Map<String, String> getAll() {
+        return Collections.unmodifiableMap(props);
+    }
+
+    public String getProperty(String key) {
+        return props.get(key);
+    }
+
+    public String getProperty(String key, String defaultValue) {
+        return props.getOrDefault(key, defaultValue);
+    }
+
+    public void setProperty(String key, String value) {
+        props.put(key, value);
+    }
+
+    public void setProperties(Map<String, String> values) {
+        props.putAll(values);
+    }
+
+    public void setProperties(Profile copyFrom) {
+        props.putAll(copyFrom.getAll());
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
similarity index 74%
rename from 
modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
rename to 
modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
index a8e09d2f3..72b69ffe7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
@@ -15,18 +15,13 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.cli.config;
-
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
+package org.apache.ignite.cli.config.ini;
 
 /**
- * Factory for {@link Config}.
+ * Exception when already created INI section trying to create.
  */
-@Factory
-public class ConfigFactory {
-    @Singleton
-    public Config fileConfig() {
-        return new Config();
+public class SectionAlreadyExistsException extends RuntimeException {
+    public SectionAlreadyExistsException(String name) {
+        super("Section " + name + " already exists.");
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java
new file mode 100644
index 000000000..0f317483c
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.ignite.cli.core.exception.handler;
+
+import org.apache.ignite.cli.config.ConfigStoringException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+
+/**
+ * Handler for {@link ConfigStoringException}.
+ */
+public class ConfigStoringExceptionHandler implements 
ExceptionHandler<ConfigStoringException> {
+    private static final IgniteLogger log = 
Loggers.forClass(ConfigStoringExceptionHandler.class);
+
+    @Override
+    public int handle(ExceptionWriter err, ConfigStoringException e) {
+        log.error("CLI config storing error: ", e);
+        err.write("Error happened while saving CLI config " + e.getMessage());
+        return 1;
+    }
+
+    @Override
+    public Class<ConfigStoringException> applicableException() {
+        return ConfigStoringException.class;
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
index ca00a9615..c63071311 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
@@ -34,5 +34,8 @@ public class DefaultExceptionHandlers extends 
ExceptionHandlers {
         addExceptionHandler(new IgniteCliExceptionHandler());
         addExceptionHandler(new IgniteCliApiExceptionHandler());
         addExceptionHandler(new UnknownCommandExceptionHandler());
+        addExceptionHandler(new ConfigStoringExceptionHandler());
+        addExceptionHandler(new ProfileNotFoundExceptionHandler());
+        addExceptionHandler(new SectionAlreadyExistsExceptionHandler());
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java
similarity index 58%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java
index ca00a9615..a410dd0bc 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java
@@ -17,22 +17,22 @@
 
 package org.apache.ignite.cli.core.exception.handler;
 
-import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+import org.apache.ignite.cli.config.ProfileNotFoundException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
 
 /**
- * Default collection of exception handlers.
+ * Handler for {@link ProfileNotFoundException}.
  */
-public class DefaultExceptionHandlers extends ExceptionHandlers {
+public class ProfileNotFoundExceptionHandler implements 
ExceptionHandler<ProfileNotFoundException> {
+    @Override
+    public int handle(ExceptionWriter err, ProfileNotFoundException e) {
+        err.write(e.getMessage());
+        return 1;
+    }
 
-    /**
-     * Constructor.
-     */
-    public DefaultExceptionHandlers() {
-        addExceptionHandler(new SqlExceptionHandler());
-        addExceptionHandler(new TimeoutExceptionHandler());
-        addExceptionHandler(new IgniteClientExceptionHandler());
-        addExceptionHandler(new IgniteCliExceptionHandler());
-        addExceptionHandler(new IgniteCliApiExceptionHandler());
-        addExceptionHandler(new UnknownCommandExceptionHandler());
+    @Override
+    public Class<ProfileNotFoundException> applicableException() {
+        return ProfileNotFoundException.class;
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java
similarity index 57%
copy from 
modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
copy to 
modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java
index ca00a9615..aa868ca8a 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java
@@ -17,22 +17,22 @@
 
 package org.apache.ignite.cli.core.exception.handler;
 
-import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+import org.apache.ignite.cli.config.ini.SectionAlreadyExistsException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
 
 /**
- * Default collection of exception handlers.
+ * Handler for {@link SectionAlreadyExistsException}.
  */
-public class DefaultExceptionHandlers extends ExceptionHandlers {
+public class SectionAlreadyExistsExceptionHandler implements 
ExceptionHandler<SectionAlreadyExistsException> {
+    @Override
+    public int handle(ExceptionWriter err, SectionAlreadyExistsException e) {
+        err.write(e.getMessage());
+        return 1;
+    }
 
-    /**
-     * Constructor.
-     */
-    public DefaultExceptionHandlers() {
-        addExceptionHandler(new SqlExceptionHandler());
-        addExceptionHandler(new TimeoutExceptionHandler());
-        addExceptionHandler(new IgniteClientExceptionHandler());
-        addExceptionHandler(new IgniteCliExceptionHandler());
-        addExceptionHandler(new IgniteCliApiExceptionHandler());
-        addExceptionHandler(new UnknownCommandExceptionHandler());
+    @Override
+    public Class<SectionAlreadyExistsException> applicableException() {
+        return SectionAlreadyExistsException.class;
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java
index 68541cfd3..2ac2c495c 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java
@@ -19,10 +19,10 @@ package org.apache.ignite.cli.core.repl;
 
 import jakarta.inject.Singleton;
 import java.util.Objects;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigConstants;
+import org.apache.ignite.cli.config.ConfigDefaultValueProvider;
 import picocli.CommandLine.IDefaultValueProvider;
 import picocli.CommandLine.Model.ArgSpec;
-import picocli.CommandLine.PropertiesDefaultProvider;
 
 /**
  * Implementation of {@link IDefaultValueProvider} based on {@link Session}.
@@ -32,23 +32,23 @@ public class SessionDefaultValueProvider implements 
IDefaultValueProvider {
 
     private final Session session;
 
-    private final IDefaultValueProvider defaultValueProvider;
+    private final ConfigDefaultValueProvider defaultValueProvider;
 
     /**
      * Constructor.
      *
      * @param session session instance.
-     * @param config config instance.
+     * @param defaultValueProvider defaults value provider.
      */
-    public SessionDefaultValueProvider(Session session, Config config) {
+    public SessionDefaultValueProvider(Session session, 
ConfigDefaultValueProvider defaultValueProvider) {
         this.session = session;
-        this.defaultValueProvider = new 
PropertiesDefaultProvider(config.getProperties());
+        this.defaultValueProvider = defaultValueProvider;
     }
 
     @Override
     public String defaultValue(ArgSpec argSpec) throws Exception {
         if (session.isConnectedToNode()) {
-            if (Objects.equals(argSpec.descriptionKey(), "ignite.jdbc-url")) {
+            if (Objects.equals(argSpec.descriptionKey(), 
ConfigConstants.JDBC_URL)) {
                 return session.getJdbcUrl();
             }
         }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java
 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java
index 4ab85fd74..812ba991f 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java
@@ -22,7 +22,7 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.StateFolderProvider;
 import 
org.apache.ignite.cli.core.exception.handler.PicocliExecutionExceptionHandler;
 import org.apache.ignite.cli.core.exception.handler.ReplExceptionHandlers;
 import org.apache.ignite.cli.core.repl.Repl;
@@ -89,7 +89,7 @@ public class ReplExecutor {
                     ? repl.getCompleter()
                     : registry.completer());
             if (repl.getHistoryFileName() != null) {
-                reader.variable(LineReader.HISTORY_FILE, new 
File(Config.getStateFolder(), repl.getHistoryFileName()));
+                reader.variable(LineReader.HISTORY_FILE, new 
File(StateFolderProvider.getStateFolder(), repl.getHistoryFileName()));
             }
 
             RegistryCommandExecutor executor = new 
RegistryCommandExecutor(registry);
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/CliCommandTestBase.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/CliCommandTestBase.java
index dfe2106a3..9bb2eec9b 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/CliCommandTestBase.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/CliCommandTestBase.java
@@ -25,6 +25,8 @@ import 
io.micronaut.test.extensions.junit5.annotation.MicronautTest;
 import jakarta.inject.Inject;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
 import org.junit.jupiter.api.BeforeEach;
 import picocli.CommandLine;
 
@@ -33,6 +35,8 @@ import picocli.CommandLine;
  */
 @MicronautTest
 public abstract class CliCommandTestBase {
+    private static final Map<Class<?>, CommandLine> cache = new HashMap<>();
+
     @Inject
     private ApplicationContext context;
 
@@ -46,7 +50,9 @@ public abstract class CliCommandTestBase {
 
     @BeforeEach
     public void setUp() {
-        cmd = new CommandLine(getCommandClass(), new 
MicronautFactory(context));
+        //Caching need to prevent bug in Picocli related to default values 
initialization.
+        //Please refer to org.apache.ignite.cli.commands.PicocliBugTest
+        cmd = cache.computeIfAbsent(getCommandClass(), clazz -> new 
CommandLine(clazz, new MicronautFactory(context)));
         sout = new StringWriter();
         serr = new StringWriter();
         cmd.setOut(new PrintWriter(sout));
@@ -55,6 +61,10 @@ public abstract class CliCommandTestBase {
 
     protected abstract Class<?> getCommandClass();
 
+    protected void execute(String argsLine) {
+        execute(argsLine.split(" "));
+    }
+
     protected void execute(String... args) {
         exitCode = cmd.execute(args);
     }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/PicocliBugTest.java 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/PicocliBugTest.java
new file mode 100644
index 000000000..13cbab49b
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/PicocliBugTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.ignite.cli.commands;
+
+import io.micronaut.configuration.picocli.MicronautFactory;
+import io.micronaut.context.ApplicationContext;
+import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Spec;
+
+//TODO: Change org.apache.ignite.cli.commands.CliCommandTestBase after fix 
https://github.com/remkop/picocli/issues/1733
+@MicronautTest
+@Disabled
+class PicocliBugTest {
+    @Inject
+    private ApplicationContext context;
+
+    private CommandLine cmd;
+
+    private StringWriter sout;
+
+    private StringWriter serr;
+
+    private int exitCode = Integer.MIN_VALUE;
+
+    @BeforeEach
+    public void setUp() {
+        cmd = new CommandLine(Command.class, new MicronautFactory(context));
+        sout = new StringWriter();
+        serr = new StringWriter();
+        cmd.setOut(new PrintWriter(sout));
+        cmd.setErr(new PrintWriter(serr));
+    }
+
+    @Test
+    public void test1() {
+        cmd.execute("--option", "option");
+        Assertions.assertEquals("option", sout.toString());
+    }
+
+    @Test
+    public void test2() {
+        cmd.execute();
+        Assertions.assertEquals("", sout.toString());
+    }
+
+    @CommandLine.Command(name = "command")
+    @Singleton
+    static class Command implements Runnable {
+        @Option(names = "--option")
+        private String option;
+
+        @Spec
+        CommandSpec spec;
+
+
+        @Override
+        public void run() {
+            spec.commandLine().getOut().print(option);
+            spec.commandLine().getOut().flush();
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigActivateCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigActivateCommandTest.java
new file mode 100644
index 000000000..526607d2b
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigActivateCommandTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.ignite.cli.commands.cliconfig;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.commands.CliCommandTestBase;
+import 
org.apache.ignite.cli.commands.cliconfig.profile.CliConfigProfileCommand;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CliConfigActivateCommandTest extends CliCommandTestBase {
+    @Inject
+    private TestConfigManagerProvider configManagerProvider;
+
+    @Override
+    protected Class<?> getCommandClass() {
+        return CliConfigProfileCommand.class;
+    }
+
+    @BeforeEach
+    public void setup() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithInternalPart());
+    }
+
+    @Test
+    public void activateTest() {
+        execute("--set-current owner");
+
+        assertAll(
+                this::assertExitCodeIsZero,
+                () -> assertOutputContains("Profile owner was activated 
successfully"),
+                this::assertErrOutputIsEmpty,
+                () -> 
assertThat(configManagerProvider.get().getCurrentProfile().getName()).isEqualTo("owner")
+        );
+    }
+
+    @Test
+    public void activateWithNotExistedProfileTest() {
+        execute("--set-current notExist");
+
+        assertAll(
+                () -> assertExitCodeIs(1),
+                () -> assertErrOutputContains("Profile notExist not found"),
+                this::assertOutputIsEmpty,
+                () -> 
assertThat(configManagerProvider.get().getCurrentProfile().getName()).isEqualTo("database")
+        );
+    }
+
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigCreateProfileCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigCreateProfileCommandTest.java
new file mode 100644
index 000000000..e2f303605
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigCreateProfileCommandTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.ignite.cli.commands.cliconfig;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.commands.CliCommandTestBase;
+import 
org.apache.ignite.cli.commands.cliconfig.profile.CliConfigCreateProfileCommand;
+import org.apache.ignite.cli.config.ProfileNotFoundException;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CliConfigCreateProfileCommandTest extends CliCommandTestBase {
+    @Inject
+    TestConfigManagerProvider configManagerProvider;
+
+    @Override
+    protected Class<?> getCommandClass() {
+        return CliConfigCreateProfileCommand.class;
+    }
+
+    @BeforeEach
+    void configManagerRefresh() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithInternalPart());
+    }
+
+    @Test
+    public void testWithoutProfileName() {
+        execute();
+
+        assertAll(
+                this::assertOutputIsEmpty,
+                () -> assertErrOutputContains("Missing required option: 
'--name=<profileName>")
+        );
+    }
+
+    @Test
+    public void testProfileCreation() {
+        execute("--name profileName");
+
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Profile profileName was created 
successfully"),
+                () -> 
assertThat(configManagerProvider.get().getCurrentProfile().getName()).isNotEqualTo("profileName")
+        );
+    }
+
+    @Test
+    public void testProfileCopyFrom() {
+        execute("--name profileName --copy-from database");
+
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Profile profileName was created 
successfully"),
+                () -> 
assertThat(configManagerProvider.get().getProfile("profileName").getAll()).containsAllEntriesOf(
+                        
configManagerProvider.get().getProfile("database").getAll())
+        );
+    }
+
+    @Test
+    public void testCopyFromNonExist() {
+        execute("--name profileName --copy-from notExist");
+
+        assertAll(
+                () -> assertExitCodeIs(1),
+                () -> assertErrOutputContains("Profile notExist not found"),
+                this::assertOutputIsEmpty,
+                () -> assertThatThrownBy(() -> 
configManagerProvider.get().getProfile("profileName"))
+                        .isInstanceOf(ProfileNotFoundException.class)
+        );
+    }
+
+    @Test
+    public void testProfileActivate() {
+        execute("--name profileName --activate");
+
+        assertAll(
+                () -> assertOutputContains("Profile profileName was created 
successfully"),
+                this::assertErrOutputIsEmpty,
+                () -> 
assertThat(configManagerProvider.get().getCurrentProfile().getName()).isEqualTo("profileName")
+        );
+    }
+
+    @Test
+    public void testCreateExistedProfile() {
+        execute("--name profileName");
+        assertAll(
+                () -> assertOutputContains("Profile profileName was created 
successfully"),
+                this::assertErrOutputIsEmpty
+        );
+
+        execute("--name profileName");
+
+        assertAll(
+                () -> assertErrOutputContains("Section profileName already 
exist")
+        );
+    }
+
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommandTest.java
index 26c4c196b..cc00e6987 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommandTest.java
@@ -19,17 +19,27 @@ package org.apache.ignite.cli.commands.cliconfig;
 
 import static org.junit.jupiter.api.Assertions.assertAll;
 
+import jakarta.inject.Inject;
 import org.apache.ignite.cli.commands.CliCommandTestBase;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 class CliConfigGetSubCommandTest extends CliCommandTestBase {
+    @Inject
+    private TestConfigManagerProvider configManagerProvider;
 
     @Override
     protected Class<?> getCommandClass() {
         return CliConfigGetSubCommand.class;
     }
 
+    @BeforeEach
+    public void setup() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithInternalPart());
+    }
+
     @Test
     @DisplayName("Key is mandatory")
     void noKey() {
@@ -47,11 +57,11 @@ class CliConfigGetSubCommandTest extends CliCommandTestBase 
{
     @DisplayName("Displays value for specified key")
     void singleKey() {
         // When executed with single key
-        execute("ignite.cluster-url");
+        execute("server");
 
         assertAll(
                 this::assertExitCodeIsZero,
-                () -> assertOutputIs("test_cluster_url" + 
System.lineSeparator()),
+                () -> assertOutputIs("127.0.0.1" + System.lineSeparator()),
                 this::assertErrOutputIsEmpty
         );
     }
@@ -81,4 +91,25 @@ class CliConfigGetSubCommandTest extends CliCommandTestBase {
                 () -> assertErrOutputContains("Unmatched argument at index 1")
         );
     }
+
+    @Test
+    public void testWithNonDefaultProfile() {
+        execute("organization --profile owner");
+
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Apache Ignite")
+        );
+
+    }
+
+    @Test
+    public void testWithNonNotExistedProfile() {
+        execute("organization --profile notExist");
+
+        assertAll(
+                this::assertOutputIsEmpty,
+                () -> assertErrOutputContains("Profile notExist not found.")
+        );
+    }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommandTest.java
index 2235ffff8..6981dd110 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommandTest.java
@@ -22,20 +22,28 @@ import static org.junit.jupiter.api.Assertions.assertAll;
 
 import jakarta.inject.Inject;
 import org.apache.ignite.cli.commands.CliCommandTestBase;
-import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.config.ConfigConstants;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 class CliConfigSetSubCommandTest extends CliCommandTestBase {
 
     @Inject
-    Config config;
+    TestConfigManagerProvider configManagerProvider;
 
     @Override
     protected Class<?> getCommandClass() {
         return CliConfigSetSubCommand.class;
     }
 
+    @BeforeEach
+    public void setup() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithInternalPart());
+    }
+
     @Test
     @DisplayName("Need at least one parameter")
     void noKey() {
@@ -66,13 +74,13 @@ class CliConfigSetSubCommandTest extends CliCommandTestBase 
{
     @DisplayName("Sets single value")
     void singleKeySet() {
         // When executed with key
-        execute("ignite.cluster-url=test");
+        execute(ConfigConstants.CLUSTER_URL + "=" + "test");
 
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertOutputIsEmpty,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertThat(config.getProperty("ignite.cluster-url")).isEqualTo("test")
+                () -> 
assertThat(configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL)).isEqualTo("test")
         );
     }
 
@@ -82,12 +90,46 @@ class CliConfigSetSubCommandTest extends CliCommandTestBase 
{
         // When executed with multiple keys
         execute("ignite.cluster-url=test", "ignite.jdbc-url=test2");
 
+        ConfigManager configManager = configManagerProvider.get();
+
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertOutputIsEmpty,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertThat(config.getProperty("ignite.cluster-url")).isEqualTo("test"),
-                () -> 
assertThat(config.getProperty("ignite.jdbc-url")).isEqualTo("test2")
+                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.CLUSTER_URL)).isEqualTo("test"),
+                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.JDBC_URL)).isEqualTo("test2")
+        );
+    }
+
+    @Test
+    @DisplayName("Set with profile")
+    public void testWithProfile() {
+        // When executed with multiple keys
+        execute("ignite.cluster-url=test ignite.jdbc-url=test2 --profile 
owner");
+
+        ConfigManager configManager = configManagerProvider.get();
+
+        assertAll(
+                this::assertExitCodeIsZero,
+                this::assertOutputIsEmpty,
+                this::assertErrOutputIsEmpty,
+                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.CLUSTER_URL)).isNotEqualTo("test"),
+                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.JDBC_URL)).isNotEqualTo("test2"),
+                () -> 
assertThat(configManager.getProfile("owner").getProperty(ConfigConstants.CLUSTER_URL)).isEqualTo("test"),
+                () -> 
assertThat(configManager.getProfile("owner").getProperty(ConfigConstants.JDBC_URL)).isEqualTo("test2"));
+    }
+
+    @Test
+    public void testWithNotExistedProfile() {
+        execute("ignite.cluster-url=test ignite.jdbc-url=test2 --profile 
notExist");
+
+        ConfigManager configManager = configManagerProvider.get();
+
+        assertAll(
+                () -> assertExitCodeIs(1),
+                this::assertOutputIsEmpty,
+                () -> assertErrOutputContains("Profile notExist not found"),
+                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.CLUSTER_URL)).isNotEqualTo("test")
         );
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigShowProfileCommandTest.java
similarity index 52%
copy from 
modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
copy to 
modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigShowProfileCommandTest.java
index b5e979e6d..34847fdca 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigShowProfileCommandTest.java
@@ -19,26 +19,45 @@ package org.apache.ignite.cli.commands.cliconfig;
 
 import static org.junit.jupiter.api.Assertions.assertAll;
 
+import jakarta.inject.Inject;
 import org.apache.ignite.cli.commands.CliCommandTestBase;
-import org.junit.jupiter.api.DisplayName;
+import 
org.apache.ignite.cli.commands.cliconfig.profile.CliConfigShowProfileCommand;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-class CliConfigSubCommandTest extends CliCommandTestBase {
+class CliConfigShowProfileCommandTest extends CliCommandTestBase {
+
+    @Inject
+    TestConfigManagerProvider configManagerProvider;
 
     @Override
     protected Class<?> getCommandClass() {
-        return CliConfigSubCommand.class;
+        return CliConfigShowProfileCommand.class;
+    }
+
+    @BeforeEach
+    void configManagerRefresh() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithInternalPart());
+    }
+
+    @Test
+    public void testWithInternalSection() {
+        execute();
+
+        assertAll(
+                () -> assertOutputContains("Current profile: database"),
+                this::assertErrOutputIsEmpty
+        );
     }
 
     @Test
-    @DisplayName("Displays all keys")
-    void noKey() {
+    public void testWithoutInternalSection() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithoutInternalPart());
         execute();
 
         assertAll(
-                this::assertExitCodeIsZero,
-                () -> 
assertOutputContains("ignite.cluster-url=test_cluster_url"),
-                () -> assertOutputContains("ignite.jdbc-url=test_jdbc_url"),
+                () -> assertOutputContains("Current profile: owner"),
                 this::assertErrOutputIsEmpty
         );
     }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
index b5e979e6d..c5505e3f2 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommandTest.java
@@ -19,12 +19,24 @@ package org.apache.ignite.cli.commands.cliconfig;
 
 import static org.junit.jupiter.api.Assertions.assertAll;
 
+import jakarta.inject.Inject;
 import org.apache.ignite.cli.commands.CliCommandTestBase;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 class CliConfigSubCommandTest extends CliCommandTestBase {
 
+    @Inject
+    TestConfigManagerProvider configManagerProvider;
+
+    @BeforeEach
+    public void setup() {
+        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithInternalPart());
+    }
+
+
     @Override
     protected Class<?> getCommandClass() {
         return CliConfigSubCommand.class;
@@ -35,10 +47,27 @@ class CliConfigSubCommandTest extends CliCommandTestBase {
     void noKey() {
         execute();
 
+        String expectedResult = "server=127.0.0.1" + System.lineSeparator()
+                + "port=8080" + System.lineSeparator()
+                + "file=\"apache.ignite\"" + System.lineSeparator();
+
+        assertAll(
+                this::assertExitCodeIsZero,
+                () -> assertOutputIs(expectedResult),
+                this::assertErrOutputIsEmpty
+        );
+    }
+
+    @Test
+    public void testWithProfile() {
+        execute("--profile owner");
+
+        String expectedResult = "name=John Smith" + System.lineSeparator()
+                + "organization=Apache Ignite" + System.lineSeparator();
+
         assertAll(
                 this::assertExitCodeIsZero,
-                () -> 
assertOutputContains("ignite.cluster-url=test_cluster_url"),
-                () -> assertOutputContains("ignite.jdbc-url=test_jdbc_url"),
+                () -> assertOutputIs(expectedResult),
                 this::assertErrOutputIsEmpty
         );
     }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/ConfigManagerTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/ConfigManagerTest.java
new file mode 100644
index 000000000..2e7058980
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/ConfigManagerTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.ignite.cli.commands.cliconfig;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import org.apache.ignite.cli.config.ini.IniConfigManager;
+import org.junit.jupiter.api.Test;
+
+class ConfigManagerTest {
+    @Test
+    public void testSaveLoadConfig() {
+        File tempFile = 
TestConfigManagerHelper.createSectionWithInternalPart();
+        IniConfigManager configManager = new IniConfigManager(tempFile);
+
+        configManager.setProperty("ignite.cluster-url", "test");
+
+        IniConfigManager configAfterSave = new IniConfigManager(tempFile);
+        
assertThat(configAfterSave.getCurrentProperty("ignite.cluster-url")).isEqualTo("test");
+    }
+
+    @Test
+    public void testLoadConfigWithoutInternalSection() {
+        File tempFile = 
TestConfigManagerHelper.createSectionWithoutInternalPart();
+        IniConfigManager configManager = new IniConfigManager(tempFile);
+
+
+        
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("owner");
+    }
+
+    @Test
+    public void testEmptyConfigLoad() {
+        File tempFile = TestConfigManagerHelper.createEmptyConfig();
+        IniConfigManager configManager = new IniConfigManager(tempFile);
+
+        
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("default");
+    }
+
+
+    @Test
+    public void testRemoveConfigFileOnRuntime() {
+        File tempFile = 
TestConfigManagerHelper.createSectionWithInternalPart();
+        IniConfigManager configManager = new IniConfigManager(tempFile);
+
+        
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("database");
+
+        assertThat(tempFile.delete()).isTrue();
+        assertThat(tempFile.exists()).isFalse();
+
+        
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("database");
+
+        configManager.setProperty("test", "testValue");
+
+        assertThat(tempFile.exists()).isTrue();
+        
assertThat(configManager.getCurrentProfile().getProperty("test")).isEqualTo("testValue");
+        
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("database");
+    }
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigFactory.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigFactory.java
deleted file mode 100644
index fb2d2b07e..000000000
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigFactory.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      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.ignite.cli.commands.cliconfig;
-
-import io.micronaut.context.annotation.Factory;
-import io.micronaut.context.annotation.Replaces;
-import jakarta.inject.Singleton;
-import java.io.File;
-import java.io.IOException;
-import org.apache.ignite.cli.config.Config;
-import org.apache.ignite.cli.config.ConfigFactory;
-
-/**
- * Test factory for {@link Config}.
- */
-@Factory
-@Replaces(factory = ConfigFactory.class)
-public class TestConfigFactory {
-
-    /**
-     * Creates a {@link Config} with some defaults for testing.
-     *
-     * @return {@link Config}
-     * @throws IOException in case temp file couldn't be created
-     */
-    @Singleton
-    public Config createConfig() throws IOException {
-        File tempFile = File.createTempFile("cli", null);
-        tempFile.deleteOnExit();
-        Config config = new Config(tempFile);
-        config.setProperty("ignite.cluster-url", "test_cluster_url");
-        config.setProperty("ignite.jdbc-url", "test_jdbc_url");
-        return config;
-    }
-}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigManagerHelper.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigManagerHelper.java
new file mode 100644
index 000000000..d4e6ab748
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigManagerHelper.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ignite.cli.commands.cliconfig;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import org.apache.ignite.cli.config.ConfigManager;
+
+/**
+ * Test factory for {@link ConfigManager}.
+ */
+public class TestConfigManagerHelper {
+    public static final String EMPTY = "empty.ini";
+    public static final String TWO_SECTION_WITH_INTERNAL_PART = 
"two_section_with_internal.ini";
+    public static final String TWO_SECTION_WITHOUT_INTERNAL_PART = 
"two_section_without_internal.ini";
+    public static final String INTEGRATION_TESTS = "integration_tests.ini";
+
+    public static File createEmptyConfig() {
+        return createIniFile(EMPTY);
+    }
+
+    public static File createSectionWithInternalPart() {
+        return createIniFile(TWO_SECTION_WITH_INTERNAL_PART);
+    }
+
+    public static File createSectionWithoutInternalPart() {
+        return createIniFile(TWO_SECTION_WITHOUT_INTERNAL_PART);
+    }
+
+    public static File createIntegrationTests() {
+        return createIniFile(INTEGRATION_TESTS);
+    }
+
+    private static File createIniFile(String iniResource) {
+        try {
+            File tempFile = File.createTempFile("cli", null);
+
+            try (FileOutputStream fileOutputStream = new 
FileOutputStream(tempFile)) {
+                FileChannel dest = fileOutputStream.getChannel();
+                InputStream resourceAsStream = 
TestConfigManagerHelper.class.getClassLoader().getResourceAsStream(iniResource);
+                ReadableByteChannel src = 
Channels.newChannel(resourceAsStream);
+                dest.transferFrom(src, 0, Integer.MAX_VALUE);
+                tempFile.deleteOnExit();
+                return tempFile;
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/ConfigTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigManagerProvider.java
similarity index 58%
rename from 
modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/ConfigTest.java
rename to 
modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigManagerProvider.java
index 1cd8e5182..ed581a665 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/ConfigTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/cli/commands/cliconfig/TestConfigManagerProvider.java
@@ -17,22 +17,22 @@
 
 package org.apache.ignite.cli.commands.cliconfig;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import io.micronaut.context.annotation.Replaces;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.config.ConfigManager;
+import org.apache.ignite.cli.config.ConfigManagerProvider;
 
-import java.io.File;
-import java.io.IOException;
-import org.apache.ignite.cli.config.Config;
-import org.junit.jupiter.api.Test;
+/**
+ * Test implementation of {@link ConfigManagerProvider}.
+ */
+@Singleton
+@Replaces(ConfigManagerProvider.class)
+public class TestConfigManagerProvider implements ConfigManagerProvider {
 
-class ConfigTest {
-    @Test
-    public void testSaveLoadConfig() throws IOException {
-        File tempFile = File.createTempFile("cli", null);
-        Config config = new Config(tempFile);
-        config.setProperty("ignite.cluster-url", "test");
-        config.saveConfig();
+    public ConfigManager configManager;
 
-        Config configAfterSave = new Config(tempFile);
-        
assertThat(configAfterSave.getProperty("ignite.cluster-url")).isEqualTo("test");
+    @Override
+    public ConfigManager get() {
+        return configManager;
     }
 }
diff --git a/modules/cli/src/test/resources/empty.ini 
b/modules/cli/src/test/resources/empty.ini
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/cli/src/test/resources/integration_tests.ini 
b/modules/cli/src/test/resources/integration_tests.ini
new file mode 100644
index 000000000..6031a5f31
--- /dev/null
+++ b/modules/cli/src/test/resources/integration_tests.ini
@@ -0,0 +1,6 @@
+[ignitecli_internal]
+current_profile = default
+
+[default]
+ignite.cluster-url = http://localhost:10300
+ignite.jdbc-url = jdbc:ignite:thin://127.0.0.1:10800
diff --git a/modules/cli/src/test/resources/two_section_with_internal.ini 
b/modules/cli/src/test/resources/two_section_with_internal.ini
new file mode 100644
index 000000000..1b125c2ff
--- /dev/null
+++ b/modules/cli/src/test/resources/two_section_with_internal.ini
@@ -0,0 +1,12 @@
+[ignitecli_internal]
+current_profile = database
+
+[owner]
+name = John Smith
+organization = Apache Ignite
+
+[database]
+server = 127.0.0.1
+port = 8080
+file = "apache.ignite"
+
diff --git a/modules/cli/src/test/resources/two_section_without_internal.ini 
b/modules/cli/src/test/resources/two_section_without_internal.ini
new file mode 100644
index 000000000..98d04ca17
--- /dev/null
+++ b/modules/cli/src/test/resources/two_section_without_internal.ini
@@ -0,0 +1,10 @@
+
+[owner]
+name = John Smith
+organization = Apache Ignite
+
+[database]
+server = 127.0.0.1
+port = 8080
+file = "apache.ignite"
+
diff --git a/parent/pom.xml b/parent/pom.xml
index 357cbe5d1..0fa404eaf 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -163,7 +163,6 @@
 
     <dependencyManagement>
         <dependencies>
-
             <dependency>
                 <groupId>io.micronaut.test</groupId>
                 <artifactId>micronaut-test-core</artifactId>
@@ -1559,6 +1558,7 @@
                         <exclude>deliveries/deb/rules</exclude>
                         <exclude>deliveries/deb/control</exclude>
                         <exclude>**/*.bat</exclude> <!-- Files in bat format 
-->
+                        <exclude>**/*.ini</exclude> <!-- Files in ini format 
-->
                         
<exclude>modules/runner/src/integrationTest/sql/**</exclude> <!-- SQL logic 
tests. -->
                     </excludes>
                 </configuration>

Reply via email to