Skip to content

Commit 8444186

Browse files
authored
CLI refactoring (#22)
* Abstract commands to classes: ValidateCommand * Add Copyright in new files * Use ValidateCommand class * Handle exit in cli_tool.py * Remove old test for validate command * Add command abstraction for Create * Update tests for update command * Add delete command * Refactor CloudFormation commands * Refactor start and stop commands * Add Upload Tasks command * Refactor exec command * Reorg commands * Reorg tests * Refactor invoke command * Refactoring Config * Refactor CLI Config * Refactor CliTool class * Fix CliTool * Update command summary to print * Fix read config with default environment settings * Re-org test files * Clean tests * Fix and test execution for Inovke command * Complete AWS profile tests and clean tests names * Move code to cloudoformation commands * Move code to CloudFormation command * Update Readme * Inject config file to CLI Tool * Fix to include template in CloudFormation commands
1 parent b5832fa commit 8444186

25 files changed

+1572
-1148
lines changed

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
![LambdaCron](./lambda-cron-diagram.png "LambdaCron")
55

66
**LambdaCron** is a serverless cron tool. It provides a way to run scheduled tasks
7-
on AWS cloud and all managed by a command line tool ([LambdaCron CLI](#lambdacron-cli)).
7+
on AWS cloud, all managed by a command line tool ([LambdaCron CLI](#lambdacron-cli)).
88

99
Tasks are scheduled using the same syntax for expressions as linux
1010
[crontab](https://help.ubuntu.com/community/CronHowto).
1111

12-
**LambdaCron** offer 3 different type of task to run:
12+
**LambdaCron** offer 4 different type of task to run:
1313

1414
* **Queue task**: send message to AWS SQS queue.
1515
* **Lambda task**: invoke AWS lambda function.
@@ -20,30 +20,30 @@ Tasks are defined in YAML files and are stored in a S3 bucket.
2020

2121
## LambdaCron CLI
2222

23-
**LambdaCron** providfe a CLI tool that allow to manage you cron tasks from you localhost,
24-
without needing to access to AWS console.
23+
**LambdaCron** provide a CLI tool that allow to manage your cron tasks from you localhost,
24+
no need to access to AWS console.
2525

2626
Also it allows to run multiple environments with different settings. As many environments
2727
as desired can be set up.
2828

2929
### Settings
3030

31-
Custom settings from environments are set in a YAML file located in user home and
32-
called **~/.lambda-cron.yml**.
31+
Custom settings for environments are set in a YAML file located in user home, it must be
32+
called: **~/.lambda-cron.yml**.
3333

3434
There are 3 levels of preferences for settings:
3535

3636
* Environment: Custom values for an specific environment.
3737
* Global: Custom values that will have effect to all environments created.
38-
* Default: Default value for options in case no custom values are specified (by environment or globally)
38+
* Default: Default values in case no custom values are specified (by environment or globally)
3939

4040
Highest level of preference is *Environment*, followed by *Global* and finally *Default*. Each option
4141
in the settings can set the value from different levels. Higher level of preference overwrite lower levels.
4242

43-
Settings are saved in a YAML file. Each environment is defined with a root key the YAML
44-
as the global settings with the key *global*.
43+
Settings are saved in a YAML file. Each environment is defined with a root key in the YAML,
44+
global settings are identified with the key *global*.
4545

46-
Following are the available options:
46+
Options available:
4747

4848
#### bucket
4949

@@ -83,7 +83,7 @@ More info for [frequency](#frequency)
8383
8484
#### alarm
8585
86-
Alarm can be set up using CloudWatch metrics. It use the following parameters:
86+
Alarm can be set up using CloudWatch metrics. It uses following parameters:
8787
8888
* enabled
8989
* email (Required if alarm is enabled).
@@ -98,7 +98,7 @@ alarm:
9898
9999
#### enabled
100100
101-
It allows to enabled/disabled the cron.
101+
It allows to enabled/disabled the cron (CloudWatch event).
102102
103103
```yaml
104104
enabled: True

lambda_cron/cli/cli_config.py

Lines changed: 82 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -19,112 +19,129 @@
1919
DEFAULT_BUCKET_PATTERN = 'lambda-cron-{environment}'
2020

2121

22-
def get_package_root_directory():
23-
return os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../'))
24-
25-
26-
def get_cli_config_file_path():
22+
def get_default_cli_config_file_path():
2723
return os.path.expanduser('~/.lambda-cron.yml')
2824

2925

30-
def get_jsonschema_file_path():
31-
return os.path.join(get_package_root_directory(), 'schema.json')
26+
class LambdaCronConfigFileNotFound(Exception):
27+
pass
3228

3329

34-
def load_config():
35-
if os.path.exists(get_cli_config_file_path()):
36-
with open(get_cli_config_file_path(), 'r') as config_file:
30+
def load_config(file_path):
31+
if os.path.exists(file_path):
32+
with open(file_path, 'r') as config_file:
3733
return yaml.load(config_file)
38-
return False
34+
raise LambdaCronConfigFileNotFound('Config file not found: {}'.format(file_path))
3935

4036

41-
class CliConfig:
37+
class CliConfigParser:
4238

43-
def __init__(self, environment):
44-
self.environment = environment
45-
self.bucket = DEFAULT_BUCKET_PATTERN.format(environment=self.environment)
46-
self.enabled = True
47-
self.alarm_enabled = False
48-
self.alarm_email = ''
49-
self.hours = 1
50-
self.minutes = False
39+
DEFAULT_ENABLED = True
40+
DEFAULT_ALARM_ENABLED = False
41+
DEFAULT_ALARM_EMAIL = ''
5142

52-
self.config = load_config()
53-
self.set_bucket()
54-
self.set_enabled()
55-
self.set_alarm()
56-
self.set_frequency()
43+
def __init__(self, config_file_path):
44+
self.file_config = load_config(config_file_path)
45+
self.environment = None
5746

58-
def is_environment_in_config_file(self):
59-
return self.config and (self.environment in self.config)
47+
def get_config(self, environment):
48+
self.environment = environment
49+
cli_config = CliConfig(self.environment)
50+
cli_config.bucket = self._get_bucket()
51+
cli_config.enabled = self._get_enabled()
52+
cli_config.alarm_enabled, cli_config.alarm_email = self._get_alarm()
53+
self._set_frequency(cli_config)
54+
return cli_config
6055

61-
def is_custom_options(self, option):
62-
return self.is_environment_in_config_file() and (option in self.config[self.environment])
56+
def _is_environment_in_config_file(self):
57+
return self.file_config and (self.environment in self.file_config)
6358

64-
def is_global_option(self, option):
65-
return self.config and ('all' in self.config) and (option in self.config['all'])
59+
def _is_custom_options(self, option):
60+
return self._is_environment_in_config_file() and (option in self.file_config[self.environment])
6661

67-
def get_config_option(self, option):
62+
def _is_global_option(self, option):
63+
return self.file_config and ('all' in self.file_config) and (option in self.file_config['all'])
64+
65+
def _get_config_option(self, option):
6866
option_value = None
69-
if self.is_custom_options(option):
70-
option_value = self.config[self.environment][option]
71-
elif self.is_global_option(option):
72-
option_value = self.config['all'][option]
67+
if self._is_custom_options(option):
68+
option_value = self.file_config[self.environment][option]
69+
elif self._is_global_option(option):
70+
option_value = self.file_config['all'][option]
7371
return option_value
7472

75-
def set_alarm(self):
76-
alarm_config = self.get_config_option('alarm')
73+
def _get_alarm(self):
74+
alarm_config = self._get_config_option('alarm')
7775

7876
if not alarm_config:
79-
return
77+
return self.DEFAULT_ALARM_ENABLED, self.DEFAULT_ALARM_EMAIL
8078

81-
self.alarm_enabled = alarm_config['enabled']
82-
if not isinstance(self.alarm_enabled, bool):
79+
alarm_enabled = alarm_config['enabled']
80+
if not isinstance(alarm_enabled, bool):
8381
raise Exception("Settings for 'alarm.enabled' must be a bool value")
84-
if self.alarm_enabled:
82+
alarm_email = self.DEFAULT_ALARM_EMAIL
83+
if alarm_enabled:
8584
if 'email' not in alarm_config:
8685
raise Exception("Email must be provided when alarm is enabled")
87-
self.alarm_email = alarm_config['email']
88-
89-
def set_bucket(self):
90-
if self.is_custom_options('bucket'):
91-
self.bucket = self.config[self.environment]['bucket']
92-
elif self.is_global_option('bucket'):
93-
if '{environment}' in self.config['all']['bucket']:
94-
self.bucket = self.config['all']['bucket'].format(environment=self.environment)
86+
alarm_email = alarm_config['email']
87+
88+
return alarm_enabled, alarm_email
89+
90+
def _get_bucket(self):
91+
if self._is_custom_options('bucket'):
92+
return self.file_config[self.environment]['bucket']
93+
elif self._is_global_option('bucket'):
94+
if '{environment}' in self.file_config['all']['bucket']:
95+
return self.file_config['all']['bucket'].format(environment=self.environment)
9596
else:
96-
self.bucket = self.config['all']['bucket'] + "-{}".format(self.environment)
97+
return self.file_config['all']['bucket'] + "-{}".format(self.environment)
98+
else:
99+
return DEFAULT_BUCKET_PATTERN.format(environment=self.environment)
97100

98-
def get_time_key_value(self, config_every):
101+
@staticmethod
102+
def _get_time_key_value(config_every):
99103
time_key = None
100104
if 'hours' in config_every:
101105
time_key = 'hours'
102106

103107
if 'minutes' in config_every:
104108
if time_key is not None:
105-
raise Exception("Only one of 'hours' or 'minutes' must be used for the frequency ('every'). Both are not allowed.")
109+
raise Exception(
110+
"Only one of 'hours' or 'minutes' must be used for the frequency ('every'). Both are not allowed.")
106111
time_key = 'minutes'
107112

108113
if time_key is None:
109114
raise Exception("Invalid time key used for frequency ('every'). Allowed keys: 'hours' or 'minutes'")
110115

111116
return time_key, int(config_every[time_key])
112117

113-
def set_frequency(self):
114-
config_every = self.get_config_option('every')
118+
def _set_frequency(self, cli_config):
119+
config_every = self._get_config_option('every')
115120
if not config_every:
116121
return
117122

118-
time_key, time_value = self.get_time_key_value(config_every)
119-
self.hours = False
120-
self.minutes = False
121-
setattr(self, time_key, time_value)
123+
cli_config.hours = cli_config.minutes = False
124+
time_key, time_value = self._get_time_key_value(config_every)
125+
setattr(cli_config, time_key, time_value)
122126

123-
def set_enabled(self):
124-
enabled_default_value = self.enabled
125-
self.enabled = self.get_config_option('enabled')
126-
if self.enabled is None:
127-
self.enabled = enabled_default_value
127+
def _get_enabled(self):
128+
enabled = self._get_config_option('enabled')
129+
if enabled is None:
130+
return self.DEFAULT_ENABLED
128131

129-
if not isinstance(self.enabled, bool):
132+
if not isinstance(enabled, bool):
130133
raise Exception("Settings for 'enabled' must be a bool value")
134+
135+
return enabled
136+
137+
138+
class CliConfig:
139+
140+
def __init__(self, environment):
141+
self.environment = environment
142+
self.bucket = DEFAULT_BUCKET_PATTERN.format(environment=self.environment)
143+
self.enabled = True
144+
self.alarm_enabled = False
145+
self.alarm_email = ''
146+
self.hours = 1
147+
self.minutes = False

0 commit comments

Comments
 (0)