Skip to content

Commit

Permalink
release v3.0.2 (#564)
Browse files Browse the repository at this point in the history
Co-authored-by: Kevin Hargita <khargita@amazon.com>
  • Loading branch information
G-Lenz and aws-khargita authored Jul 24, 2024
1 parent 98ddb98 commit 73fa071
Show file tree
Hide file tree
Showing 58 changed files with 3,001 additions and 3,624 deletions.
2 changes: 1 addition & 1 deletion .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { PythonProject } from "projen/lib/python";

function main() {
new InstanceScheduler({ version: "3.0.1", cdkVersion: "2.130.0" }).synth();
new InstanceScheduler({ version: "3.0.2", cdkVersion: "2.130.0" }).synth();
}

interface InstanceSchedulerProps {
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [3.0.2] -- 2024-07-24

### Fixed
- Fixed an error that caused CloudFormation-managed schedules using the (now deprecated) UseMaintenanceWindow flag be an un-updatable

### Security
- Upgrade Certifi to mitigate CVE-2024-39689



## [3.0.1] -- 2024-06-27

### Changed
Expand Down
3,665 changes: 1,878 additions & 1,787 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import copy
import datetime
import time
from collections.abc import Mapping
from dataclasses import dataclass, field
from typing import Any, Optional, TypedDict
from typing import Optional, TypedDict
from zoneinfo import ZoneInfo

from instance_scheduler import configuration
from instance_scheduler.configuration.instance_schedule import InstanceSchedule
from instance_scheduler.util.app_env import get_app_env
from instance_scheduler.util.time import is_aware


Expand Down Expand Up @@ -61,40 +59,3 @@ def get_time_from_string(timestr: Optional[str]) -> Optional[datetime.time]:
except ValueError:
return None
return datetime.time(tm.tm_hour, tm.tm_min, 0)


def build_tags_from_template(
tags_str: Any, tag_variables: Optional[Any] = None
) -> list[TagTemplate]:
lastkey = None
tags = {}
for tag in tags_str.split(","):
if "=" in tag:
t = tag.partition("=")
tags[t[0]] = t[2]
lastkey = t[0]
elif lastkey is not None:
tags[lastkey] = ",".join([tags[lastkey], tag])

tag_vars = {} if tag_variables is None else copy.copy(tag_variables)

dt = datetime.datetime.now(datetime.timezone.utc)
tag_vars.update(
{
configuration.TAG_VAL_SCHEDULER: get_app_env().stack_name,
configuration.TAG_VAL_YEAR: "{:0>4d}".format(dt.year),
configuration.TAG_VAL_MONTH: "{:0>2d}".format(dt.month),
configuration.TAG_VAL_DAY: "{:0>2d}".format(dt.day),
configuration.TAG_VAL_HOUR: "{:0>2d}".format(dt.hour),
configuration.TAG_VAL_MINUTE: "{:0>2d}".format(dt.minute),
configuration.TAG_VAL_TIMEZONE: "UTC",
}
)

for tag in tags:
value = tags[tag]
if value not in ["", None]:
for v in tag_vars:
tags[tag] = tags[tag].replace("{{{}}}".format(v), tag_vars[v])

return [{"Key": t, "Value": tags[t]} for t in tags]
8 changes: 6 additions & 2 deletions source/app/instance_scheduler/handler/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Generic, TypeGuard, TypeVar

from instance_scheduler.handler.environments.main_lambda_environment import (
MainLambdaEnv,
)

if TYPE_CHECKING:
from aws_lambda_powertools.utilities.typing import LambdaContext
else:
Expand All @@ -12,14 +16,14 @@
T = TypeVar("T")


class Handler(ABC, Generic[T]):
class MainHandler(ABC, Generic[T]):
@classmethod
@abstractmethod
def is_handling_request(cls, event: Mapping[str, Any]) -> TypeGuard[T]:
pass

@abstractmethod
def __init__(self, event: T, context: LambdaContext) -> None:
def __init__(self, event: T, context: LambdaContext, env: MainLambdaEnv) -> None:
pass

@abstractmethod
Expand Down
62 changes: 36 additions & 26 deletions source/app/instance_scheduler/handler/cfn_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

from botocore.exceptions import ClientError

from instance_scheduler.handler.environments.main_lambda_environment import (
MainLambdaEnv,
)
from instance_scheduler.model.period_definition import PeriodDefinition
from instance_scheduler.model.period_identifier import PeriodIdentifier
from instance_scheduler.model.schedule_definition import ScheduleDefinition
Expand All @@ -15,7 +18,6 @@
from instance_scheduler.model.store.dynamo_schedule_definition_store import (
DynamoScheduleDefinitionStore,
)
from instance_scheduler.util.app_env import get_app_env
from instance_scheduler.util.custom_resource import (
CustomResource,
CustomResourceRequest,
Expand Down Expand Up @@ -80,7 +82,6 @@ class CfnScheduleResourceProperties(TypedDict, total=False):
RetainRunning: NotRequired[str]
StopNewInstances: NotRequired[str]
SsmMaintenanceWindow: NotRequired[list[str] | str]
Metrics: NotRequired[str]
OverrideStatus: NotRequired[str]
Periods: NotRequired[list[CfnSchedulePeriodProperties]]

Expand All @@ -98,30 +99,29 @@ def __init__(
self,
event: CustomResourceRequest[CfnScheduleResourceProperties],
context: LambdaContext,
env: MainLambdaEnv,
) -> None:
"""
Initializes instance
:param event: CFN event
:param context: Lambda context
"""
CustomResource.__init__(self, event, context)
self._logger = self._init_logger()
app_env = get_app_env()
self.schedule_store = DynamoScheduleDefinitionStore(app_env.config_table_name)
self.period_store = DynamoPeriodDefinitionStore(app_env.config_table_name)
self._logger = self._init_logger(env)
self.schedule_store = DynamoScheduleDefinitionStore(env.config_table_name)
self.period_store = DynamoPeriodDefinitionStore(env.config_table_name)

def _init_logger(self) -> Logger:
app_env = get_app_env()
def _init_logger(self, env: MainLambdaEnv) -> Logger:
classname = self.__class__.__name__
dt = datetime.now(timezone.utc)
log_stream = "{}-{:0>4d}{:0>2d}{:0>2d}".format(
classname, dt.year, dt.month, dt.day
)
return Logger(
log_group=app_env.log_group,
log_group=env.log_group,
log_stream=log_stream,
topic_arn=app_env.topic_arn,
debug=app_env.enable_debug_logging,
topic_arn=env.topic_arn,
debug=env.enable_debug_logging,
)

@staticmethod
Expand Down Expand Up @@ -200,6 +200,7 @@ def _update_request(self) -> CustomResourceResponse:
schedule_def, period_defs = self._parse_schedule_template_item(
self.resource_properties
)

old_sched_def, _ = self._parse_schedule_template_item(
self.old_resource_properties
)
Expand Down Expand Up @@ -274,7 +275,7 @@ def _parse_schedule_template_item(
self, resource_properties: CfnScheduleResourceProperties
) -> tuple[ScheduleDefinition, list[PeriodDefinition]]:
# ---------------- Validation ----------------#
_validate_schedule_props_structure(resource_properties)
self._validate_schedule_props_structure(resource_properties)
for period_props in resource_properties.get("Periods", []):
_validate_period_props_structure(period_props)

Expand Down Expand Up @@ -345,6 +346,29 @@ def _parse_schedule_template_item(

return sche_def, period_defs

def _validate_schedule_props_structure(
self, props: CfnScheduleResourceProperties
) -> None:
for key in props.keys():
if key in {"ServiceToken", "Timeout"}:
# these properties used to be part of the sample template in the IG, but have been removed in July 2023,
# They do not do anything, but customers may still have old templates that include them,
# so we need to not break compatibility
continue

if key in {"UseMaintenanceWindow", "Metrics"}:
# deprecated keys that no longer do anything but that not should throw errors
self._logger.warning(
f'Schedule contains deprecated field "${key}", this field will be ignored.'
)
continue

if key not in CfnScheduleResourceProperties.__annotations__.keys():
raise InvalidScheduleConfiguration(
f"Unknown schedule property {key}, valid properties are "
f"{CfnScheduleResourceProperties.__annotations__.keys()}"
)


def _parse_bool(bool_str: Optional[str]) -> Optional[bool]:
if bool_str is None:
Expand Down Expand Up @@ -384,17 +408,3 @@ def _validate_period_props_structure(props: CfnSchedulePeriodProperties) -> None
f"Unknown period property {key}, valid properties are "
f"{CfnSchedulePeriodProperties.__annotations__.keys()}"
)


def _validate_schedule_props_structure(props: CfnScheduleResourceProperties) -> None:
for key in props.keys():
if key in {"ServiceToken", "Timeout"}:
# these properties used to be part of the sample template in the IG, but have been removed in July 2023,
# They do not do anything, but customers may still have old templates that include them,
# so we need to not break compatibility
continue
if key not in CfnScheduleResourceProperties.__annotations__.keys():
raise InvalidScheduleConfiguration(
f"Unknown schedule property {key}, valid properties are "
f"{CfnScheduleResourceProperties.__annotations__.keys()}"
)
27 changes: 13 additions & 14 deletions source/app/instance_scheduler/handler/cli/cli_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
from packaging.version import Version

from instance_scheduler import __version__
from instance_scheduler.handler.base import Handler
from instance_scheduler.handler.base import MainHandler
from instance_scheduler.handler.cli.schedule_usage import get_schedule_usage
from instance_scheduler.handler.environments.main_lambda_environment import (
MainLambdaEnv,
)
from instance_scheduler.model.ddb_item_utils import optionally
from instance_scheduler.model.period_definition import (
PeriodDefinition,
Expand Down Expand Up @@ -41,7 +44,6 @@
)
from instance_scheduler.ops_metrics.metrics import collect_metric
from instance_scheduler.util import safe_json
from instance_scheduler.util.app_env import get_app_env
from instance_scheduler.util.logger import Logger
from instance_scheduler.util.validation import ValidationException, validate_string

Expand Down Expand Up @@ -72,36 +74,33 @@ class UnsupportedVersionException(Exception):
pass


class CliRequestHandler(Handler[AdminCliRequest]):
class CliRequestHandler(MainHandler[AdminCliRequest]):
"""
Class to handles requests from admin CLI
"""

def __init__(self, event: AdminCliRequest, context: LambdaContext) -> None:
def __init__(
self, event: AdminCliRequest, context: LambdaContext, env: MainLambdaEnv
) -> None:
"""
Initializes handle instance
:param event: event to handle
:param context: lambda context
"""
self._event = event
self._context = context
self._schedule_store = DynamoScheduleDefinitionStore(
get_app_env().config_table_name
)
self._period_store = DynamoPeriodDefinitionStore(
get_app_env().config_table_name
)
self._schedule_store = DynamoScheduleDefinitionStore(env.config_table_name)
self._period_store = DynamoPeriodDefinitionStore(env.config_table_name)

# Setup logging
classname = self.__class__.__name__
app_env = get_app_env()
dt = datetime.now(timezone.utc)
log_stream = LOG_STREAM.format(classname, dt.year, dt.month, dt.day)
self._logger = Logger(
log_group=app_env.log_group,
log_group=env.log_group,
log_stream=log_stream,
topic_arn=app_env.topic_arn,
debug=app_env.enable_debug_logging,
topic_arn=env.topic_arn,
debug=env.enable_debug_logging,
)

@property
Expand Down
Loading

0 comments on commit 73fa071

Please sign in to comment.