Repository: localstack/localstack

Issue:
implement service provider fallback
This issue has shown that, when we introduce new providers and propagate the configs to users, then remove the config ("asf" was removed and renamed to "default"), user configuration breaks
- #6358

One way of dealing with this is checking at startup time whether the configured providers are available, and if not, log a warning and fall back to the default provider.


Discussion:
wdyt @dfangl ?
I think its a good idea, especially for CI environments where this can be quite unclear.
There should be a warning logged already, in https://github.com/localstack/localstack/blob/61a1bbcb9a70be6d4ced28fc979ea68654c02acf/localstack/services/plugins.py#L588-L594 . I think all we need to do might be replacing the `return None` with `provider = self.get_default_provider()` (which could just extract the default provider from the provider config).

Resolving patch:
diff --git a/localstack/services/plugins.py b/localstack/services/plugins.py
--- a/localstack/services/plugins.py
+++ b/localstack/services/plugins.py
@@ -495,13 +495,28 @@ def __init__(
         self.provider_config = provider_config or config.SERVICE_PROVIDER_CONFIG
 
     def get_active_provider(self, service: str) -> str:
+        """
+        Get configured provider for a given service
+
+        :param service: Service name
+        :return: configured provider
+        """
         return self.provider_config.get_provider(service)
 
+    def get_default_provider(self) -> str:
+        """
+        Get the default provider
+
+        :return: default provider
+        """
+        return self.provider_config.default_value
+
     # TODO make the abstraction clearer, to provide better information if service is available versus discoverable
     # especially important when considering pro services
     def list_available(self) -> List[str]:
         """
         List all available services, which have an available, configured provider
+
         :return: List of service names
         """
         return [
@@ -513,6 +528,7 @@ def list_available(self) -> List[str]:
     def list_loaded_services(self) -> List[str]:
         """
         Lists all the services which have a provider that has been initialized
+
         :return: a list of service names
         """
         return [
@@ -585,13 +601,17 @@ def _load_service_plugin(self, name: str) -> Optional[ServicePlugin]:
         if preferred_provider in providers:
             provider = preferred_provider
         else:
+            default = self.get_default_provider()
             LOG.warning(
-                "Configured provider (%s) does not exist for service (%s). Available options are: %s",
+                "Configured provider (%s) does not exist for service (%s). Available options are: %s. "
+                "Falling back to default provider '%s'. This can impact the availability of Pro functionality, "
+                "please fix this configuration issue as soon as possible.",
                 preferred_provider,
                 name,
                 providers,
+                default,
             )
-            return None
+            provider = default
 
         plugin_name = f"{name}:{provider}"
         plugin = self.plugin_manager.load(plugin_name)


Test patch: