Skip to content

Commit 495c634

Browse files
committed
fix: prevent past ttl values for environment and snapshot
1 parent bdff229 commit 495c634

2 files changed

Lines changed: 65 additions & 2 deletions

File tree

sqlmesh/core/config/root.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from sqlmesh.core.loader import Loader, SqlMeshLoader
4242
from sqlmesh.core.notification_target import NotificationTarget
4343
from sqlmesh.core.user import User
44+
from sqlmesh.utils.date import to_timestamp, now, now_timestamp
4445
from sqlmesh.utils.errors import ConfigError
4546
from sqlmesh.utils.pydantic import field_validator, model_validator
4647

@@ -97,8 +98,8 @@ class Config(BaseConfig):
9798
default_gateway: str = ""
9899
notification_targets: t.List[NotificationTarget] = []
99100
project: str = ""
100-
snapshot_ttl: str = c.DEFAULT_SNAPSHOT_TTL
101-
environment_ttl: t.Optional[str] = c.DEFAULT_ENVIRONMENT_TTL
101+
snapshot_ttl: NoPastTTLString = c.DEFAULT_SNAPSHOT_TTL
102+
environment_ttl: t.Optional[NoPastTTLString] = c.DEFAULT_ENVIRONMENT_TTL
102103
ignore_patterns: t.List[str] = c.IGNORE_PATTERNS
103104
time_column_format: str = c.DEFAULT_TIME_COLUMN_FORMAT
104105
users: t.List[User] = []
@@ -302,3 +303,20 @@ def dialect(self) -> t.Optional[str]:
302303
@property
303304
def fingerprint(self) -> str:
304305
return str(zlib.crc32(pickle.dumps(self.dict(exclude={"loader", "notification_targets"}))))
306+
307+
308+
def validate_no_past_ttl(v: str) -> str:
309+
current_time = now()
310+
if to_timestamp(v, relative_base=current_time) < to_timestamp(current_time):
311+
raise ValueError(
312+
f"TTL '{v}' is in the past. Please specify a relative time in the future. Ex: `in 1 week` instead of `1 week`."
313+
)
314+
return v
315+
316+
317+
if t.TYPE_CHECKING:
318+
NoPastTTLString = str
319+
else:
320+
from pydantic.functional_validators import BeforeValidator
321+
322+
NoPastTTLString = t.Annotated[str, BeforeValidator(validate_no_past_ttl)]

tests/integrations/github/cicd/test_config.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,48 @@ def test_validation(tmp_path):
200200
ValueError, match="merge_method must be set if enable_deploy_command is True"
201201
):
202202
load_config_from_paths(Config, project_paths=[tmp_path / "config.yaml"])
203+
204+
205+
def test_ttl_in_past(tmp_path):
206+
create_temp_file(
207+
tmp_path,
208+
pathlib.Path("config.yaml"),
209+
"""
210+
environment_ttl: in 1 week
211+
model_defaults:
212+
dialect: duckdb
213+
""",
214+
)
215+
216+
config = load_config_from_paths(Config, project_paths=[tmp_path / "config.yaml"])
217+
assert config.environment_ttl == "in 1 week"
218+
219+
create_temp_file(
220+
tmp_path,
221+
pathlib.Path("config.yaml"),
222+
"""
223+
environment_ttl: 1 week
224+
model_defaults:
225+
dialect: duckdb
226+
""",
227+
)
228+
with pytest.raises(
229+
ValueError,
230+
match="TTL '1 week' is in the past. Please specify a relative time in the future. Ex: `in 1 week` instead of `1 week`.",
231+
):
232+
load_config_from_paths(Config, project_paths=[tmp_path / "config.yaml"])
233+
234+
create_temp_file(
235+
tmp_path,
236+
pathlib.Path("config.yaml"),
237+
"""
238+
snapshot_ttl: 1 week
239+
model_defaults:
240+
dialect: duckdb
241+
""",
242+
)
243+
with pytest.raises(
244+
ValueError,
245+
match="TTL '1 week' is in the past. Please specify a relative time in the future. Ex: `in 1 week` instead of `1 week`.",
246+
):
247+
load_config_from_paths(Config, project_paths=[tmp_path / "config.yaml"])

0 commit comments

Comments
 (0)