Skip to content

Commit d283818

Browse files
committed
Merge pull request #16 from rackerlabs/feature_alias
Added support for aliasing function uploads
2 parents 4f67dc0 + e0a5d8a commit d283818

File tree

3 files changed

+178
-44
lines changed

3 files changed

+178
-44
lines changed

lambda_uploader/config.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
'region': basestring, 'handler': basestring,
2020
'role': basestring, 'timeout': int, 'memory': int}
2121

22-
DEFAULT_PARAMS = {u'requirements': [], u'publish': False}
22+
DEFAULT_PARAMS = {u'requirements': [], u'publish': False,
23+
u'alias': None, u'alias_description': None}
2324

2425

2526
class Config(object):
@@ -32,21 +33,44 @@ def __init__(self, pth):
3233
for param, clss in REQUIRED_PARAMS.iteritems():
3334
self._validate(param, cls=clss)
3435

36+
'''
37+
Return raw config
38+
'''
3539
@property
3640
def raw(self):
3741
if not self._config:
3842
self._load_config()
3943

4044
return self._config
4145

46+
'''
47+
Return an alias description if set otherwise return an the function
48+
description
49+
'''
50+
@property
51+
def alias_description(self):
52+
if self._config['alias_description'] is None:
53+
return self._config['description']
54+
else:
55+
return self._config['alias_description']
56+
57+
'''Set the publish attr to true'''
4258
def set_publish(self):
4359
self._config['publish'] = True
4460

61+
'''Set the alias and description'''
62+
def set_alias(self, alias, description=None):
63+
self._config['alias'] = alias
64+
self._config['alias_description'] = description
65+
self._config['publish'] = True
66+
67+
'''Set all defaults after loading the config'''
4568
def _set_defaults(self):
4669
for param, val in DEFAULT_PARAMS.iteritems():
4770
if self._config.get(param) is None:
4871
self._config[param] = val
4972

73+
'''Validate the configuration file'''
5074
def _validate(self, key, cls=None):
5175
if key not in self._config:
5276
raise ValueError("Config %s must have %s set"
@@ -56,6 +80,7 @@ def _validate(self, key, cls=None):
5680
raise TypeError("Config value '%s' should be %s not %s"
5781
% (key, cls, type(self._config[key])))
5882

83+
'''Load config ... called by init()'''
5984
def _load_config(self):
6085
config_file = path.join(self._path, 'lambda.json')
6186
if not path.isfile(config_file):

lambda_uploader/shell.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ def _execute(args):
4848
pth = path.abspath(args.function_dir)
4949

5050
cfg = config.Config(pth)
51-
# Set publish if flagged to do so
52-
if args.publish:
53-
cfg.set_publish()
5451

5552
_print('Building Package')
5653
pkg = package.build_package(pth, cfg.requirements)
@@ -59,8 +56,23 @@ def _execute(args):
5956
pkg.clean_workspace()
6057

6158
if not args.no_upload:
59+
# Set publish if flagged to do so
60+
if args.publish:
61+
cfg.set_publish()
62+
63+
create_alias = False
64+
# Set alias if the arg is passed
65+
if args.alias is not None:
66+
cfg.set_alias(args.alias, args.alias_description)
67+
create_alias = True
68+
6269
_print('Uploading Package')
63-
uploader.upload_package(pkg, cfg, args.profile)
70+
upldr = uploader.PackageUploader(cfg, args.profile)
71+
upldr.upload(pkg)
72+
# If the alias was set create it
73+
if create_alias:
74+
upldr.alias()
75+
6476
pkg.clean_zipfile()
6577

6678
_print('Fin')
@@ -88,6 +100,11 @@ def main(arv=None):
88100
const=True)
89101
parser.add_argument('--profile', dest='profile',
90102
help='specify AWS cli profile')
103+
alias_help = 'alias for published version (WILL SET THE PUBLISH FLAG)'
104+
parser.add_argument('--alias', '-a', dest='alias',
105+
default=None, help=alias_help)
106+
parser.add_argument('--alias-description', '-m', dest='alias_description',
107+
default=None, help='alias description')
91108
parser.add_argument('function_dir', default=getcwd(), nargs='?',
92109
help='lambda function directory')
93110

lambda_uploader/uploader.py

Lines changed: 131 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,52 +18,144 @@
1818
LOG = logging.getLogger(__name__)
1919

2020

21-
def upload_package(pkg, config, profile_name):
22-
with open(pkg.zip_file, "rb") as fil:
23-
zip_file = fil.read()
24-
25-
session = boto3.session.Session(region_name=config.region,
26-
profile_name=profile_name)
27-
client = session.client('lambda')
28-
# Assume the function already exists in AWS
29-
existing_function = True
30-
31-
try:
32-
get_resp = client.get_function_configuration(FunctionName=config.name)
33-
LOG.debug("AWS get_function_configuration response: %s" % get_resp)
34-
except:
35-
existing_function = False
36-
LOG.debug("function not found creating new function")
37-
38-
if existing_function:
21+
class PackageUploader(object):
22+
def __init__(self, config, profile_name):
23+
self._config = config
24+
session = boto3.session.Session(region_name=config.region,
25+
profile_name=profile_name)
26+
self._client = session.client('lambda')
27+
self.version = None
28+
29+
'''
30+
Calls the AWS methods to upload an existing package and update
31+
the function configuration
32+
33+
returns the package version
34+
'''
35+
def upload_existing(self, pkg):
36+
with open(pkg.zip_file, "rb") as fil:
37+
zip_file = fil.read()
38+
3939
LOG.debug('running update_function_code')
40-
response = client.update_function_code(
41-
FunctionName=config.name,
40+
conf_update_resp = self._client.update_function_code(
41+
FunctionName=self._config.name,
4242
ZipFile=zip_file,
43-
Publish=config.publish,
43+
Publish=False,
4444
)
45-
LOG.debug("AWS update_function_code response: %s" % response)
45+
LOG.debug("AWS update_function_code response: %s"
46+
% conf_update_resp)
4647
LOG.debug('running update_function_configuration')
47-
response = client.update_function_configuration(
48-
FunctionName=config.name,
49-
Handler=config.handler,
50-
Role=config.role,
51-
Description=config.description,
52-
Timeout=config.timeout,
53-
MemorySize=config.memory,
48+
response = self._client.update_function_configuration(
49+
FunctionName=self._config.name,
50+
Handler=self._config.handler,
51+
Role=self._config.role,
52+
Description=self._config.description,
53+
Timeout=self._config.timeout,
54+
MemorySize=self._config.memory,
5455
)
55-
LOG.debug("AWS update_function_configuration response: %s" % response)
56-
else:
56+
LOG.debug("AWS update_function_configuration response: %s"
57+
% response)
58+
59+
version = response.get('Version')
60+
# Publish the version after upload and config update if needed
61+
if self._config.publish:
62+
resp = self._client.publish_version(
63+
FunctionName=self._config.name,
64+
)
65+
LOG.debug("AWS publish_version response: %s" % resp)
66+
version = resp.get('Version')
67+
68+
return version
69+
70+
'''
71+
Creates and uploads a new lambda function
72+
73+
returns the package version
74+
'''
75+
def upload_new(self, pkg):
76+
with open(pkg.zip_file, "rb") as fil:
77+
zip_file = fil.read()
78+
5779
LOG.debug('running create_function_code')
58-
response = client.create_function(
59-
FunctionName=config.name,
80+
response = self._client.create_function(
81+
FunctionName=self._config.name,
6082
Runtime='python2.7',
61-
Handler=config.handler,
62-
Role=config.role,
83+
Handler=self._config.handler,
84+
Role=self._config.role,
6385
Code={'ZipFile': zip_file},
64-
Description=config.description,
65-
Timeout=config.timeout,
66-
MemorySize=config.memory,
67-
Publish=config.publish,
86+
Description=self._config.description,
87+
Timeout=self._config.timeout,
88+
MemorySize=self._config.memory,
89+
Publish=self._config.publish,
6890
)
6991
LOG.debug("AWS create_function response: %s" % response)
92+
93+
return response.get('Version')
94+
95+
'''
96+
Auto determines whether the function exists or not and calls
97+
the appropriate method (upload_existing or upload_new).
98+
'''
99+
def upload(self, pkg):
100+
existing_function = True
101+
try:
102+
get_resp = self._client.get_function_configuration(
103+
FunctionName=self._config.name)
104+
LOG.debug("AWS get_function_configuration response: %s" % get_resp)
105+
except:
106+
existing_function = False
107+
LOG.debug("function not found creating new function")
108+
109+
if existing_function:
110+
self.version = self.upload_existing(pkg)
111+
else:
112+
self.version = self.upload_new(pkg)
113+
114+
'''
115+
Create/update an alias to point to the package. Raises an
116+
exception if the package has not been uploaded.
117+
'''
118+
def alias(self):
119+
# if self.version is still None raise exception
120+
if self.version is None:
121+
raise Exception('Must upload package before applying alias')
122+
123+
if self._alias_exists():
124+
self._update_alias()
125+
else:
126+
self._create_alias()
127+
128+
'''
129+
Pulls down the current list of aliases and checks to see if
130+
an alias exists.
131+
'''
132+
def _alias_exists(self):
133+
resp = self._client.list_aliases(
134+
FunctionName=self._config.name)
135+
136+
for alias in resp.get('Aliases'):
137+
if alias.get('Name') == self._config.alias:
138+
return True
139+
return False
140+
141+
'''Creates alias'''
142+
def _create_alias(self):
143+
LOG.debug("Creating new alias %s" % self._config.alias)
144+
resp = self._client.create_alias(
145+
FunctionName=self._config.name,
146+
Name=self._config.alias,
147+
FunctionVersion=self.version,
148+
Description=self._config.alias_description,
149+
)
150+
LOG.debug("AWS create_alias response: %s" % resp)
151+
152+
'''Update alias'''
153+
def _update_alias(self):
154+
LOG.debug("Updating alias %s" % self._config.alias)
155+
resp = self._client.update_alias(
156+
FunctionName=self._config.name,
157+
Name=self._config.alias,
158+
FunctionVersion=self.version,
159+
Description=self._config.alias_description,
160+
)
161+
LOG.debug("AWS update_alias response: %s" % resp)

0 commit comments

Comments
 (0)