Skip to content

Commit cc31937

Browse files
committed
Fixed isdir() and isfile() relative paths in the build method, which was resulting in False being returned when deploy is run outside of the source directory. Added cleaning up of the local 'dist' directory containing the zipped deployment packages, as this was just filling up with deployments unnecessarily. Added a check in the build method to detect if the src has been specified with a trailing forward slash, as this was causing source_directories to be incorrectly named when deployed to aws (they were missing the first character of the directory name). Added a check for the file permissions when running on Mac/Linux, as aws lambda inherits the local file permissions and this was causing a permissions issue on service.py during lambda execution, as it did not have execute permissions (changed this for any .py files in the package)..
1 parent aad2dad commit cc31937

File tree

1 file changed

+93
-26
lines changed

1 file changed

+93
-26
lines changed

aws_lambda/aws_lambda.py

Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,38 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import print_function
33

4-
import hashlib
5-
import json
6-
import logging
74
import os
85
import sys
6+
import yaml
97
import time
10-
from collections import defaultdict
11-
from imp import load_source
12-
from shutil import copy
13-
from shutil import copyfile
14-
from shutil import copystat
15-
from shutil import copytree
16-
from tempfile import mkdtemp
17-
8+
import json
189
import boto3
10+
import hashlib
11+
import logging
1912
import botocore
20-
import yaml
13+
import platform
2114
import subprocess
15+
from stat import ST_MODE
16+
from imp import load_source
17+
from tempfile import mkdtemp
18+
from collections import defaultdict
2219

23-
from .helpers import archive
24-
from .helpers import get_environment_variable_value
25-
from .helpers import mkdir
26-
from .helpers import read
27-
from .helpers import timestamp
28-
from .helpers import LambdaContext
29-
20+
from shutil import (
21+
copy,
22+
copyfile,
23+
copystat,
24+
copytree,
25+
rmtree
26+
)
27+
28+
from .helpers import (
29+
read,
30+
mkdir,
31+
archive,
32+
timestamp,
33+
LambdaContext,
34+
get_environment_variable_value
35+
)
3036

3137
ARN_PREFIXES = {
3238
'cn-north-1': 'aws-cn',
@@ -120,6 +126,11 @@ def deploy(
120126
else:
121127
create_function(cfg, path_to_zip_file)
122128

129+
print('Cleaning up build from local filesystem')
130+
dist_directory = cfg.get('dist_directory', 'dist')
131+
path_to_dist = os.path.join(src, dist_directory)
132+
rmtree(path_to_dist)
133+
123134

124135
def deploy_s3(
125136
src, requirements=None, local_package=None,
@@ -158,6 +169,7 @@ def deploy_s3(
158169
create_function(cfg, path_to_zip_file, use_s3=use_s3, s3_file=s3_file)
159170

160171

172+
161173
def upload(
162174
src, requirements=None, local_package=None,
163175
config_file='config.yaml', profile_name=None,
@@ -268,6 +280,52 @@ def init(src, minimal=False):
268280
if not os.path.isdir(dest_path):
269281
copy(dest_path, src)
270282

283+
def check_object_permissions(path):
284+
# Fixes an issue when building on mac/linux, where local file permissions are inherited by aws lambda
285+
# causing a permissions error to be thrown when aws lambda tries to execute service.py and read other
286+
# objects in the build.
287+
288+
# Only required on Mac and Linux
289+
if platform.system() in ('Darwin', 'Linux'):
290+
291+
def check(path):
292+
current_octal_mode = int(oct(os.stat(path)[ST_MODE])[-3:])
293+
user_octal_mode, group_octal_mode, others_octal_mode = [int(m) for m in str(current_octal_mode)]
294+
295+
# Ensure we have both read and execute permissions on .py files
296+
if os.path.isfile(path) and os.path.splitext(os.path.split(path)[1])[1] == '.py':
297+
if group_octal_mode not in (5, 7):# 5 == read and execute, 7 == read, write and execute
298+
group_octal_mode = 5
299+
if others_octal_mode not in (5, 7):# 5 == read and execute, 7 == read, write and execute
300+
others_octal_mode = 5
301+
302+
new_octal_mode = int(f'{user_octal_mode}{group_octal_mode}{others_octal_mode}')
303+
if new_octal_mode != current_octal_mode:
304+
print(f'Changing permissions on \'{path}\' from octal mode {current_octal_mode} to {new_octal_mode}')
305+
os.chmod(path, new_octal_mode)
306+
307+
# Ensure we have read permissions on all other files
308+
elif others_octal_mode < 4:
309+
# 'Others' do not have read and execute permissions, grant this now
310+
others_octal_mode = 4
311+
new_octal_mode = int(f'{user_octal_mode}{group_octal_mode}{others_octal_mode}')
312+
print(f'Changing permissions on \'{path}\' from octal mode {current_octal_mode} to {new_octal_mode}')
313+
os.chmod(path, new_octal_mode)
314+
315+
if os.path.isfile(path):
316+
# Check file has the appropriate permissions
317+
check(path)
318+
elif os.path.isdir(path):
319+
# # Check directory itself has the appropriate permissions
320+
# check(path)
321+
322+
# Check the contents of the directory have the appropriate permissions
323+
for root, dirs, files in os.walk(path):
324+
for d in dirs:
325+
check(os.path.join(root, d))
326+
for f in files:
327+
check(os.path.join(root, f))
328+
271329

272330
def build(
273331
src, requirements=None, local_package=None,
@@ -336,16 +394,17 @@ def build(
336394

337395
files = []
338396
for filename in os.listdir(src):
339-
if os.path.isfile(filename):
397+
file_path = os.path.join(src, filename)
398+
if os.path.isfile(file_path):
340399
if filename == '.DS_Store':
341400
continue
342401
if filename == config_file:
343402
continue
344403
print('Bundling: %r' % filename)
345-
files.append(os.path.join(src, filename))
346-
elif os.path.isdir(filename) and filename in source_directories:
404+
files.append(file_path)
405+
elif os.path.isdir(file_path) and filename in source_directories:
347406
print('Bundling directory: %r' % filename)
348-
files.append(os.path.join(src, filename))
407+
files.append(file_path)
349408

350409
# "cd" into `temp_path` directory.
351410
os.chdir(path_to_temp)
@@ -354,15 +413,23 @@ def build(
354413
_, filename = os.path.split(f)
355414

356415
# Copy handler file into root of the packages folder.
357-
copyfile(f, os.path.join(path_to_temp, filename))
358-
copystat(f, os.path.join(path_to_temp, filename))
416+
file_path = os.path.join(path_to_temp, filename)
417+
copyfile(f, file_path)
418+
copystat(f, file_path)
359419
elif os.path.isdir(f):
360-
destination_folder = os.path.join(path_to_temp, f[len(src) + 1:])
420+
destination_loc = f[len(src) + 1:] if not src.endswith('/') else f[len(src):]
421+
destination_folder = os.path.join(path_to_temp, destination_loc)
361422
copytree(f, destination_folder)
362423

424+
425+
426+
# Check the permissions on all objects in the build
427+
check_object_permissions(path='./')
428+
363429
# Zip them together into a single file.
364430
# TODO: Delete temp directory created once the archive has been compiled.
365431
path_to_zip_file = archive('./', path_to_dist, output_filename)
432+
366433
return path_to_zip_file
367434

368435

0 commit comments

Comments
 (0)