Repository: dbt-labs/dbt-core

Issue:
[LMA Regression] Reduce strictness of validation for Redshift IAM targets
### Describe the bug
In previous releases of dbt, the `password` and `iam_duration_seconds` properties of Redshift targets using the `iam` method were optional. In 0.15.0-rc1, they are now required. This is a regression: both `iam_duration_seconds` and `password` should be optional properties when connecting to a Redshift cluster using an IAM role.

### Steps To Reproduce
Use a profile like:
```
redshift:
  target: iam
  outputs:
    iam:
      type: redshift
      method: iam
      cluster_id: my_cluster_id
      host: somedb.somehost.com
      user: drew
      port: 5439
      dbname: analytics
      schema: dbt_dbanin
      threads: 1
```

I can provide working creds to debug this in development.

One thing to note: Making the `password` field optional in the Redshift Connection does something funky behind the scenes - I see `TypeError: non-default argument 'port' follows default argument` from deep inside the dataclasses lib.


Discussion:

Resolving patch:
diff --git a/plugins/postgres/dbt/adapters/postgres/connections.py b/plugins/postgres/dbt/adapters/postgres/connections.py
--- a/plugins/postgres/dbt/adapters/postgres/connections.py
+++ b/plugins/postgres/dbt/adapters/postgres/connections.py
@@ -16,10 +16,10 @@
 class PostgresCredentials(Credentials):
     host: str
     user: str
-    password: str
     port: Port
-    search_path: Optional[str]
-    keepalives_idle: Optional[int] = 0  # 0 means to use the default value
+    password: str  # on postgres the password is mandatory
+    search_path: Optional[str] = None
+    keepalives_idle: int = 0  # 0 means to use the default value
 
     _ALIASES = {
         'dbname': 'database',
diff --git a/plugins/redshift/dbt/adapters/redshift/connections.py b/plugins/redshift/dbt/adapters/redshift/connections.py
--- a/plugins/redshift/dbt/adapters/redshift/connections.py
+++ b/plugins/redshift/dbt/adapters/redshift/connections.py
@@ -39,13 +39,14 @@ class RedshiftConnectionMethod(StrEnum):
 @dataclass
 class RedshiftCredentials(PostgresCredentials):
     method: RedshiftConnectionMethod = RedshiftConnectionMethod.DATABASE
+    password: Optional[str] = None
     cluster_id: Optional[str] = field(
         default=None,
         metadata={'description': 'If using IAM auth, the name of the cluster'},
     )
-    iam_duration_seconds: Optional[int] = None
+    iam_duration_seconds: int = 900
     search_path: Optional[str] = None
-    keepalives_idle: Optional[int] = 240
+    keepalives_idle: int = 240
 
     @property
     def type(self):
@@ -133,6 +134,12 @@ def get_credentials(cls, credentials):
         # Support missing 'method' for backwards compatibility
         if method == 'database' or method is None:
             logger.debug("Connecting to Redshift using 'database' credentials")
+            # this requirement is really annoying to encode into json schema,
+            # so validate it here
+            if credentials.password is None:
+                raise dbt.exceptions.FailedToConnectException(
+                    "'password' field is required for 'database' credentials"
+                )
             return credentials
 
         elif method == 'iam':


Test patch:
diff --git a/test/unit/test_redshift_adapter.py b/test/unit/test_redshift_adapter.py
--- a/test/unit/test_redshift_adapter.py
+++ b/test/unit/test_redshift_adapter.py
@@ -83,6 +83,26 @@ def test_explicit_iam_conn(self):
         expected_creds = self.config.credentials.replace(password='tmp_password')
         self.assertEqual(creds, expected_creds)
 
+    def test_iam_conn_optionals(self):
+
+        profile_cfg = {
+            'outputs': {
+                'test': {
+                    'type': 'redshift',
+                    'dbname': 'redshift',
+                    'user': 'root',
+                    'host': 'thishostshouldnotexist',
+                    'port': 5439,
+                    'schema': 'public',
+                    'method': 'iam',
+                    'cluster_id': 'my_redshift',
+                }
+            },
+            'target': 'test'
+        }
+
+        config_from_parts_or_dicts(self.config, profile_cfg)
+
     def test_invalid_auth_method(self):
         # we have to set method this way, otherwise it won't validate
         self.config.credentials.method = 'badmethod'
@@ -199,7 +219,7 @@ def test_set_zero_keepalive(self, psycopg2):
             password='password',
             port=5439,
             connect_timeout=10)
-    
+
     def test_dbname_verification_is_case_insensitive(self):
         # Override adapter settings from setUp()
         profile_cfg = {
@@ -229,5 +249,5 @@ def test_dbname_verification_is_case_insensitive(self):
         }
         self.config = config_from_parts_or_dicts(project_cfg, profile_cfg)
         self.adapter.cleanup_connections()
-        self._adapter = RedshiftAdapter(self.config) 
+        self._adapter = RedshiftAdapter(self.config)
         self.adapter.verify_database('redshift')