Repository: conan-io/conan

Issue:
[bug] Docker runner does not honor custom container name
### Describe the bug

When trying to use official ConanDockerTools images with the new feature [Docker runners](https://docs.conan.io/2/reference/runners/docker.html), I wanted to use a custom container name (e.g conan_linux_gcc11), however, the current behavior uses image name instead and ignores my configuration file.

```
conan create all --version=1.0.1 -pr linux-gcc11                                                                                                                     

======== Exporting recipe to the cache ========
paho-mqtt-cpp/1.0.1: Exporting package recipe: /Users/uilian/Development/conan/conan-center-index/recipes/paho-mqtt-cpp/all/conanfile.py
paho-mqtt-cpp/1.0.1: exports: File 'conandata.yml' found. Exporting it...
paho-mqtt-cpp/1.0.1: Calling export_sources()
paho-mqtt-cpp/1.0.1: Copied 1 '.py' file: conanfile.py
paho-mqtt-cpp/1.0.1: Copied 1 '.yml' file: conandata.yml
paho-mqtt-cpp/1.0.1: Copied 1 '.patch' file: 1.0.1-0001-fix-cmake.patch
paho-mqtt-cpp/1.0.1: Exported to cache folder: /Users/uilian/.conan2/p/paho-c2a77d934f3de/e
paho-mqtt-cpp/1.0.1: Exported: paho-mqtt-cpp/1.0.1#f7c87fa70a8dc4d8274efeb500f73320 (2024-05-10 10:03:04 UTC)

======== Input profiles ========
Profile host:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
[conf]


Profile build:
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.cppstd=17
compiler.libcxx=libc++
compiler.version=15
os=Macos
[conf]



┌───────────────────────────────┐
| Creating the docker container |
└───────────────────────────────┘

ERROR: Imposible to run the container "conanio/gcc11-ubuntu16.04:2.3.0" with image "conanio/gcc11-ubuntu16.04:2.3.0"

400 Client Error for http+docker://localhost/v1.43/containers/create?name=conanio%2Fgcc11-ubuntu16.04%3A2.3.0: Bad Request ("Invalid container name (conanio/gcc11-ubuntu16.04:2.3.0), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed")
```


The 🐛 is because the code is passing the image name instead of the container name to Docker:

https://github.com/conan-io/conan/blob/2.3.0/conan/internal/runner/docker.py#L114

🍔 

/cc @davidsanfal 

#### Environment 

OS: OSX 14.4.1 (23E224)
Arch: Mac M1 MAX
Conan version: 2.3.0
Docker version: 25.0.4-rd
Rancher version: 1.13.1

### How to reproduce it

To reproduce this error in Mac, I need a profile file and a configuration file, plus, the docker image downloaded.

```
# ~/.conan2/profiles/linux-gcc11
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux

[runner]
type=docker
cache=clean
remove=true
configfile=/Users/uilian/.conan2/runners/linux-gcc11
```

```
# ~/.conan2/runners/linux-gcc11
image: conanio/gcc11-ubuntu16.04:2.3.0
run:
    name: conan_linux_gcc11
    containerUser: conan
    privileged: False
```

Once you have these files in your Conan cache folder, you will need to run:

```
docker pull conanio/gcc11-ubuntu16.04:2.3.0
conan new cmake_lib -d name=foo -d version=0.1.0
conan create . -pr:b default -pr:h linux-gcc11
```

(I ran docker pull separated only to illustrate)

Discussion:

Patch:
diff --git a/conan/internal/api/detect_api.py b/conan/internal/api/detect_api.py
index 5f2eabebd..e90894b29 100644
--- a/conan/internal/api/detect_api.py
+++ b/conan/internal/api/detect_api.py
@@ -384,9 +384,14 @@ def detect_default_compiler():
         return None, None, None
 
     if platform.system() == "Windows":
-        compiler, version, compiler_exe = detect_msvc_compiler()
-        if compiler:
-            return compiler, version, compiler_exe
+        ide_version = _detect_vs_ide_version()
+        version = {"17": "193", "16": "192", "15": "191"}.get(str(ide_version))  # Map to compiler
+        if ide_version == "17":
+            update = detect_msvc_update(version)  # FIXME weird passing here the 193 compiler version
+            if update and int(update) >= 10:
+                version = "194"
+        if version:
+            return 'msvc', Version(version), None
 
     if platform.system() == "SunOS":
         sun_cc, sun_cc_version, compiler_exe = detect_suncc_compiler()
@@ -409,7 +414,7 @@ def detect_default_compiler():
 
 
 def default_msvc_ide_version(version):
-    version = {"193": "17", "192": "16", "191": "15"}.get(str(version))
+    version = {"194": "17", "193": "17", "192": "16", "191": "15"}.get(str(version))
     if version:
         return Version(version)
 
diff --git a/conan/internal/runner/docker.py b/conan/internal/runner/docker.py
index ebc6374e3..77aabaf0a 100644
--- a/conan/internal/runner/docker.py
+++ b/conan/internal/runner/docker.py
@@ -90,7 +90,7 @@ class DockerRunner:
             profile_list = set(self.args.profile_build + self.args.profile_host)
         else:
             profile_list = self.args.profile_host or self.args.profile_build
-        
+
         # Update the profile paths
         for i, raw_arg in enumerate(raw_args):
             for i, raw_profile in enumerate(profile_list):
@@ -111,7 +111,7 @@ class DockerRunner:
         if not (self.dockerfile or self.image):
             raise ConanException("'dockerfile' or docker image name is needed")
         self.image = self.image or 'conan-runner-default'
-        self.name = self.configfile.image or f'conan-runner-{host_profile.runner.get("suffix", "docker")}'
+        self.name = self.configfile.run.name or f'conan-runner-{host_profile.runner.get("suffix", "docker")}'
         self.remove = str(host_profile.runner.get('remove', 'false')).lower() == 'true'
         self.cache = str(host_profile.runner.get('cache', 'clean'))
         self.container = None
diff --git a/conan/tools/microsoft/visual.py b/conan/tools/microsoft/visual.py
index 8c929e979..1632e3b95 100644
--- a/conan/tools/microsoft/visual.py
+++ b/conan/tools/microsoft/visual.py
@@ -59,7 +59,8 @@ def msvc_version_to_vs_ide_version(version):
                 '190': '14',
                 '191': '15',
                 '192': '16',
-                '193': '17'}
+                '193': '17',
+                '194': '17'}  # Note both 193 and 194 belong to VS 17 2022
     return _visuals[str(version)]
 
 
@@ -75,7 +76,8 @@ def msvc_version_to_toolset_version(version):
                 '190': 'v140',
                 '191': 'v141',
                 '192': 'v142',
-                "193": 'v143'}
+                "193": 'v143',
+                "194": 'v143'}
     return toolsets.get(str(version))
 
 
@@ -140,6 +142,7 @@ class VCVars:
                 compiler_update = conanfile.settings.get_safe("compiler.update", "")
                 # The equivalent of compiler 19.26 is toolset 14.26
                 vcvars_ver = "14.{}{}".format(compiler_version[-1], compiler_update)
+
         vcvarsarch = _vcvars_arch(conanfile)
 
         winsdk_version = conanfile.conf.get("tools.microsoft:winsdk_version", check_type=str)
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 15836051d..129ceb76f 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -94,14 +94,14 @@ compiler:
                     "10", "10.1", "10.2", "10.3", "10.4", "10.5",
                     "11", "11.1", "11.2", "11.3", "11.4",
                     "12", "12.1", "12.2", "12.3",
-                    "13", "13.1", "13.2", 
+                    "13", "13.1", "13.2",
                     "14", "14.1"]
         libcxx: [libstdc++, libstdc++11]
         threads: [null, posix, win32]  # Windows MinGW
         exception: [null, dwarf2, sjlj, seh]  # Windows MinGW
         cppstd: [null, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
     msvc:
-        version: [170, 180, 190, 191, 192, 193]
+        version: [170, 180, 190, 191, 192, 193, 194]
         update: [null, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
         runtime: [static, dynamic]
         runtime_type: [Debug, Release]
diff --git a/conans/client/conf/detect_vs.py b/conans/client/conf/detect_vs.py
index f0c48c749..4db783a87 100644
--- a/conans/client/conf/detect_vs.py
+++ b/conans/client/conf/detect_vs.py
@@ -12,7 +12,7 @@ def vs_installation_path(version):
 
 
 def vs_detect_update(version):
-    version = {"193": "17", "192": "16", "191": "15"}.get(str(version))
+    version = {"194": "17", "193": "17", "192": "16", "191": "15"}.get(str(version))
     full_version = _vs_installation_path(version)[1]
     components = full_version.split(".")
     if len(components) > 1:
diff --git a/conans/client/graph/compatibility.py b/conans/client/graph/compatibility.py
index c72c4377c..967ddc90e 100644
--- a/conans/client/graph/compatibility.py
+++ b/conans/client/graph/compatibility.py
@@ -30,26 +30,43 @@ from conan.errors import ConanException
 def cppstd_compat(conanfile):
     # It will try to find packages with all the cppstd versions
     extension_properties = getattr(conanfile, "extension_properties", {})
-    if extension_properties.get("compatibility_cppstd") is False:
-        return []
     compiler = conanfile.settings.get_safe("compiler")
     compiler_version = conanfile.settings.get_safe("compiler.version")
     cppstd = conanfile.settings.get_safe("compiler.cppstd")
-    if not compiler or not compiler_version or not cppstd:
+    if not compiler or not compiler_version:
         return []
     base = dict(conanfile.settings.values_list)
-    cppstd_possible_values = supported_cppstd(conanfile)
-    if cppstd_possible_values is None:
-        conanfile.output.warning(f'No cppstd compatibility defined for compiler "{compiler}"')
-        return []
-    ret = []
-    for _cppstd in cppstd_possible_values:
-        if _cppstd is None or _cppstd == cppstd:
+    factors = []  # List of list, each sublist is a potential combination
+    if cppstd is not None and extension_properties.get("compatibility_cppstd") is not False:
+        cppstd_possible_values = supported_cppstd(conanfile)
+        if cppstd_possible_values is None:
+            conanfile.output.warning(f'No cppstd compatibility defined for compiler "{compiler}"')
+        else:
+            factors.append([{"compiler.cppstd": v} for v in cppstd_possible_values if v != cppstd])
+
+    if compiler == "msvc":
+        msvc_fallback = {"194": "193"}.get(compiler_version)
+        if msvc_fallback:
+            factors.append([{"compiler.version": msvc_fallback}])
+
+    combinations = []
+    for factor in factors:
+        if not combinations:
+            combinations = factor
             continue
+        new_combinations = []
+        for comb in combinations:
+            for f in factor:
+                comb = comb.copy()
+                comb.update(f)
+                new_combinations.append(comb)
+        combinations = new_combinations
+
+    ret = []
+    for comb in combinations:
         configuration = base.copy()
-        configuration["compiler.cppstd"] = _cppstd
+        configuration.update(comb)
         ret.append({"settings": [(k, v) for k, v in configuration.items()]})
-
     return ret
 """
 


Test patch:
diff --git a/conans/test/integration/command/runner_test.py b/conans/test/integration/command/runner_test.py
index 5341d7309..f809236bd 100644
--- a/conans/test/integration/command/runner_test.py
+++ b/conans/test/integration/command/runner_test.py
@@ -141,7 +141,8 @@ def test_create_docker_runner_dockerfile_file_path():
     client.save({"host": profile_host, "build": profile_build})
     client.run("new cmake_lib -d name=pkg -d version=0.2")
     client.run("create . -pr:h host -pr:b build")
-    print(client.out)
+
+    assert "Container conan-runner-docker running" in client.out
     assert "Restore: pkg/0.2" in client.out
     assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe" in client.out
     assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe metadata" in client.out
@@ -249,7 +250,7 @@ def test_create_docker_runner_profile_abs_path():
     cache=copy
     remove=True
     """)
-    
+
     client.save({"host": profile_host, "build": profile_build})
     client.run("new cmake_lib -d name=pkg -d version=0.2")
     client.run(f"create . -pr:h '{os.path.join(client.current_folder, 'host')}' -pr:b '{os.path.join(client.current_folder, 'build')}'")
@@ -272,6 +273,8 @@ def test_create_docker_runner_profile_abs_path_from_configfile():
         build:
             dockerfile: {dockerfile_path("Dockerfile_test")}
             build_context: {conan_base_path()}
+        run:
+            name: my-custom-conan-runner-container
         """)
     client.save({"configfile.yaml": configfile})
 
@@ -301,11 +304,12 @@ def test_create_docker_runner_profile_abs_path_from_configfile():
     cache=copy
     remove=True
     """)
-    
+
     client.save({"host": profile_host, "build": profile_build})
     client.run("new cmake_lib -d name=pkg -d version=0.2")
     client.run(f"create . -pr:h '{os.path.join(client.current_folder, 'host')}' -pr:b '{os.path.join(client.current_folder, 'build')}'")
 
+    assert "Container my-custom-conan-runner-container running" in client.out
     assert "Restore: pkg/0.2" in client.out
     assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe" in client.out
     assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe metadata" in client.out
@@ -357,11 +361,11 @@ def test_create_docker_runner_profile_abs_path_from_configfile_with_args():
     cache=copy
     remove=True
     """)
-    
+
     client.save({"host": profile_host, "build": profile_build})
     client.run("new cmake_lib -d name=pkg -d version=0.2")
     client.run(f"create . -pr:h '{os.path.join(client.current_folder, 'host')}' -pr:b '{os.path.join(client.current_folder, 'build')}'")
-    print(client.out)
+
     assert "test/integration/command/dockerfiles/Dockerfile_args" in client.out
     assert "Restore: pkg/0.2" in client.out
     assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe" in client.out
@@ -434,4 +438,4 @@ def test_create_docker_runner_default_build_profile_error():
     client.save({"host_clean": profile_host})
     client.run("new cmake_lib -d name=pkg -d version=0.2")
     with pytest.raises(Exception, match="ERROR: The default build profile '/root/.conan2/profiles/default' doesn't exist.") as exception:
-        client.run("create . -pr:h host_clean")
\ No newline at end of file
+        client.run("create . -pr:h host_clean")
diff --git a/conans/test/integration/package_id/test_cache_compatibles.py b/conans/test/integration/package_id/test_cache_compatibles.py
index f98dd7fe6..702b11e6d 100644
--- a/conans/test/integration/package_id/test_cache_compatibles.py
+++ b/conans/test/integration/package_id/test_cache_compatibles.py
@@ -377,6 +377,27 @@ class TestDefaultCompat:
         c.run("install --requires=app/0.1  --build=missing")
         assert re.search(r"Skipped binaries(\s*)tool/0.1", c.out)
 
+    def test_msvc_194_fallback(self):
+        c = TestClient()
+        save(c.cache.default_profile_path, "")
+        c.save({"conanfile.py": GenConanfile("mylib", "1.0").with_settings("os", "arch",
+                                                                           "compiler", "build_type"),
+                "profile_build": "[settings]\nos=Windows\narch=x86_64"})
+
+        c.run("create . -s os=Windows -s arch=x86_64 -s build_type=Release "
+              "-s compiler=msvc "
+              "-s compiler.version=193 -s compiler.cppstd=17 "
+              "-s compiler.runtime=dynamic -pr:b=profile_build")
+        package_id1 = c.created_package_id("mylib/1.0")
+
+        # Try to install with cppstd 14, it will find cppstd 17 as compatible
+        c.run("install --requires=mylib/1.0@ -s os=Windows -s arch=x86_64 -s build_type=Release "
+              "-s compiler=msvc "
+              "-s compiler.version=194 -s compiler.cppstd=14 "
+              "-s compiler.runtime=dynamic -pr:b=profile_build")
+        assert "mylib/1.0: Main binary package 'e340edd75790e7156c595edebd3d98b10a2e091e' missing."\
+               f"Using compatible package '{package_id1}'"
+
 
 class TestErrorsCompatibility:
     """ when the plugin fails, we want a clear message and a helpful trace
diff --git a/conans/test/unittests/tools/cmake/test_cmaketoolchain.py b/conans/test/unittests/tools/cmake/test_cmaketoolchain.py
index b08dc13b7..8bba79509 100644
--- a/conans/test/unittests/tools/cmake/test_cmaketoolchain.py
+++ b/conans/test/unittests/tools/cmake/test_cmaketoolchain.py
@@ -194,7 +194,7 @@ def test_osx_deployment_target(conanfile_apple):
 def conanfile_msvc():
     c = ConanFile(None)
     c.settings = Settings({"os": ["Windows"],
-                           "compiler": {"msvc": {"version": ["193"], "cppstd": ["20"],
+                           "compiler": {"msvc": {"version": ["193", "194"], "cppstd": ["20"],
                                                  "update": [None, 8, 9]}},
                            "build_type": ["Release"],
                            "arch": ["x86"]})
@@ -226,6 +226,14 @@ def test_toolset_update_version(conanfile_msvc):
     assert 'set(CMAKE_GENERATOR_TOOLSET "v143,version=14.38" CACHE STRING "" FORCE)' in toolchain.content
 
 
+def test_toolset_update_version_overflow(conanfile_msvc):
+    # https://github.com/conan-io/conan/issues/15583
+    conanfile_msvc.settings.compiler.version = "194"
+    conanfile_msvc.settings.compiler.update = "8"
+    toolchain = CMakeToolchain(conanfile_msvc)
+    assert 'set(CMAKE_GENERATOR_TOOLSET "v143,version=14.48" CACHE STRING "" FORCE)' in toolchain.content
+
+
 def test_toolset_x64(conanfile_msvc):
     # https://github.com/conan-io/conan/issues/11144
     conanfile_msvc.conf.define("tools.cmake.cmaketoolchain:toolset_arch", "x64")
diff --git a/conans/test/unittests/tools/microsoft/test_msvs_toolset.py b/conans/test/unittests/tools/microsoft/test_msvs_toolset.py
index b5f8b1a5d..14574127e 100644
--- a/conans/test/unittests/tools/microsoft/test_msvs_toolset.py
+++ b/conans/test/unittests/tools/microsoft/test_msvs_toolset.py
@@ -15,6 +15,7 @@ def test_invalid_compiler():
 
 
 @pytest.mark.parametrize("compiler_version,expected_toolset", [
+    ("194", "v143"),
     ("193", "v143"),
     ("192", "v142"),
     ("191", "v141"),