11# -*- coding: utf-8 -*-
22from __future__ import print_function
33
4- import hashlib
5- import json
6- import logging
74import os
85import sys
6+ import yaml
97import 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
189import boto3
10+ import hashlib
11+ import logging
1912import botocore
20- import yaml
13+ import platform
2114import 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
3137ARN_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
124135def 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+
161173def 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
272330def 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