dpl-connect 1.8.43

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +8 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +36 -0
  6. data/Gemfile +100 -0
  7. data/LICENSE +22 -0
  8. data/README.md +934 -0
  9. data/Rakefile +1 -0
  10. data/TESTING.md +29 -0
  11. data/bin/dpl +5 -0
  12. data/dpl.gemspec +32 -0
  13. data/lib/dpl/cli.rb +66 -0
  14. data/lib/dpl/error.rb +3 -0
  15. data/lib/dpl/provider.rb +264 -0
  16. data/lib/dpl/provider/anynines.rb +13 -0
  17. data/lib/dpl/provider/appfog.rb +21 -0
  18. data/lib/dpl/provider/atlas.rb +108 -0
  19. data/lib/dpl/provider/azure_webapps.rb +48 -0
  20. data/lib/dpl/provider/bintray.rb +509 -0
  21. data/lib/dpl/provider/bitballoon.rb +22 -0
  22. data/lib/dpl/provider/bluemix_cloud_foundry.rb +23 -0
  23. data/lib/dpl/provider/boxfuse.rb +57 -0
  24. data/lib/dpl/provider/catalyze.rb +49 -0
  25. data/lib/dpl/provider/chef_supermarket.rb +85 -0
  26. data/lib/dpl/provider/cloud66.rb +38 -0
  27. data/lib/dpl/provider/cloud_files.rb +38 -0
  28. data/lib/dpl/provider/cloud_foundry.rb +43 -0
  29. data/lib/dpl/provider/code_deploy.rb +123 -0
  30. data/lib/dpl/provider/deis.rb +119 -0
  31. data/lib/dpl/provider/divshot.rb +23 -0
  32. data/lib/dpl/provider/elastic_beanstalk.rb +195 -0
  33. data/lib/dpl/provider/engine_yard.rb +90 -0
  34. data/lib/dpl/provider/firebase.rb +27 -0
  35. data/lib/dpl/provider/gae.rb +97 -0
  36. data/lib/dpl/provider/gcs.rb +59 -0
  37. data/lib/dpl/provider/hackage.rb +29 -0
  38. data/lib/dpl/provider/heroku.rb +18 -0
  39. data/lib/dpl/provider/heroku/api.rb +98 -0
  40. data/lib/dpl/provider/heroku/generic.rb +94 -0
  41. data/lib/dpl/provider/heroku/git.rb +28 -0
  42. data/lib/dpl/provider/lambda.rb +236 -0
  43. data/lib/dpl/provider/launchpad.rb +48 -0
  44. data/lib/dpl/provider/modulus.rb +23 -0
  45. data/lib/dpl/provider/npm.rb +64 -0
  46. data/lib/dpl/provider/openshift.rb +59 -0
  47. data/lib/dpl/provider/ops_works.rb +132 -0
  48. data/lib/dpl/provider/packagecloud.rb +144 -0
  49. data/lib/dpl/provider/pages.rb +79 -0
  50. data/lib/dpl/provider/puppet_forge.rb +43 -0
  51. data/lib/dpl/provider/pypi.rb +111 -0
  52. data/lib/dpl/provider/releases.rb +139 -0
  53. data/lib/dpl/provider/rubygems.rb +51 -0
  54. data/lib/dpl/provider/s3.rb +123 -0
  55. data/lib/dpl/provider/scalingo.rb +97 -0
  56. data/lib/dpl/provider/script.rb +29 -0
  57. data/lib/dpl/provider/surge.rb +33 -0
  58. data/lib/dpl/provider/testfairy.rb +190 -0
  59. data/lib/dpl/provider/transifex.rb +45 -0
  60. data/lib/dpl/version.rb +3 -0
  61. data/notes/engine_yard.md +1 -0
  62. data/notes/heroku.md +3 -0
  63. data/spec/cli_spec.rb +36 -0
  64. data/spec/provider/anynines_spec.rb +20 -0
  65. data/spec/provider/appfog_spec.rb +35 -0
  66. data/spec/provider/atlas_spec.rb +99 -0
  67. data/spec/provider/azure_webapps_spec.rb +95 -0
  68. data/spec/provider/bintray_spec.rb +259 -0
  69. data/spec/provider/bitballoon_spec.rb +32 -0
  70. data/spec/provider/bluemixcloudfoundry_spec.rb +23 -0
  71. data/spec/provider/boxfuse_spec.rb +16 -0
  72. data/spec/provider/catalyze_spec.rb +39 -0
  73. data/spec/provider/chef_supermarket_spec.rb +51 -0
  74. data/spec/provider/cloud66_spec.rb +44 -0
  75. data/spec/provider/cloud_files_spec.rb +88 -0
  76. data/spec/provider/cloudfoundry_spec.rb +71 -0
  77. data/spec/provider/code_deploy_spec.rb +360 -0
  78. data/spec/provider/deis_spec.rb +116 -0
  79. data/spec/provider/divshot_spec.rb +28 -0
  80. data/spec/provider/elastic_beanstalk_spec.rb +209 -0
  81. data/spec/provider/firebase_spec.rb +40 -0
  82. data/spec/provider/gae_spec.rb +26 -0
  83. data/spec/provider/gcs_spec.rb +115 -0
  84. data/spec/provider/hackage_spec.rb +47 -0
  85. data/spec/provider/heroku_spec.rb +357 -0
  86. data/spec/provider/lambda_spec.rb +432 -0
  87. data/spec/provider/launchpad_spec.rb +33 -0
  88. data/spec/provider/modulus_spec.rb +29 -0
  89. data/spec/provider/npm_spec.rb +95 -0
  90. data/spec/provider/openshift_spec.rb +91 -0
  91. data/spec/provider/ops_works_spec.rb +127 -0
  92. data/spec/provider/packagecloud_spec.rb +56 -0
  93. data/spec/provider/puppet_forge_spec.rb +60 -0
  94. data/spec/provider/pypi_spec.rb +103 -0
  95. data/spec/provider/releases_spec.rb +303 -0
  96. data/spec/provider/rubygems_spec.rb +106 -0
  97. data/spec/provider/s3_spec.rb +174 -0
  98. data/spec/provider/scalingo_spec.rb +64 -0
  99. data/spec/provider/script_spec.rb +26 -0
  100. data/spec/provider/surge_spec.rb +15 -0
  101. data/spec/provider/testfairy_spec.rb +86 -0
  102. data/spec/provider/transifex_spec.rb +110 -0
  103. data/spec/provider_spec.rb +210 -0
  104. data/spec/spec_helper.rb +20 -0
  105. metadata +279 -0
@@ -0,0 +1,97 @@
1
+ module DPL
2
+ class Provider
3
+ class GAE < Provider
4
+ experimental 'Google App Engine'
5
+
6
+ BASE='https://dl.google.com/dl/cloudsdk/channels/rapid/'
7
+ NAME='google-cloud-sdk'
8
+ EXT='.tar.gz'
9
+ INSTALL='~'
10
+ BOOTSTRAP="#{INSTALL}/#{NAME}/bin/bootstrapping/install.py"
11
+ GCLOUD="#{INSTALL}/#{NAME}/bin/gcloud"
12
+
13
+ def with_python_2_7(cmd)
14
+ cmd.gsub!(/'/, "'\\\\''")
15
+ context.shell("bash -c 'source #{context.env['HOME']}/virtualenv/python2.7/bin/activate; #{cmd}'")
16
+ end
17
+
18
+ def install_deploy_dependencies
19
+ if File.exists? GCLOUD
20
+ return
21
+ end
22
+
23
+ $stderr.puts 'Python 2.7 Version'
24
+
25
+ unless with_python_2_7("python -c 'import sys; print(sys.version)'")
26
+ error 'Could not use python2.7'
27
+ end
28
+
29
+ $stderr.puts 'Downloading Google Cloud SDK ...'
30
+
31
+ unless context.shell("curl -L #{BASE + NAME + EXT} | gzip -d | tar -x -C #{INSTALL}")
32
+ error 'Could not download Google Cloud SDK.'
33
+ end
34
+
35
+ $stderr.puts 'Bootstrapping Google Cloud SDK ...'
36
+
37
+ unless with_python_2_7("#{BOOTSTRAP} --usage-reporting=false --command-completion=false --path-update=false")
38
+ error 'Could not bootstrap Google Cloud SDK.'
39
+ end
40
+ end
41
+
42
+ def needs_key?
43
+ false
44
+ end
45
+
46
+ def check_auth
47
+ unless with_python_2_7("#{GCLOUD} -q auth activate-service-account --key-file #{keyfile}")
48
+ error 'Authentication failed.'
49
+ end
50
+ end
51
+
52
+ def keyfile
53
+ options[:keyfile] || context.env['GOOGLECLOUDKEYFILE'] || 'service-account.json'
54
+ end
55
+
56
+ def project
57
+ options[:project] || context.env['GOOGLECLOUDPROJECT'] || context.env['CLOUDSDK_CORE_PROJECT'] || File.dirname(context.env['TRAVIS_REPO_SLUG'] || '')
58
+ end
59
+
60
+ def version
61
+ options[:version]
62
+ end
63
+
64
+ def config
65
+ options[:config] || 'app.yaml'
66
+ end
67
+
68
+ def no_promote
69
+ options[:no_promote]
70
+ end
71
+
72
+ def verbosity
73
+ options[:verbosity] || 'warning'
74
+ end
75
+
76
+ def no_stop_previous_version
77
+ options[:no_stop_previous_version]
78
+ end
79
+
80
+ def push_app
81
+ command = GCLOUD
82
+ command << ' --quiet'
83
+ command << " --verbosity \"#{verbosity}\""
84
+ command << " --project \"#{project}\""
85
+ command << " app deploy \"#{config}\""
86
+ command << " --version \"#{version}\"" unless version.to_s.empty?
87
+ command << " --#{no_promote ? 'no-' : ''}promote"
88
+ command << ' --no-stop-previous-version' unless no_stop_previous_version.to_s.empty?
89
+ unless with_python_2_7(command)
90
+ log 'Deployment failed.'
91
+ context.shell('find $HOME/.config/gcloud/logs -type f -print -exec cat {} \;')
92
+ error ''
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,59 @@
1
+ require 'kconv'
2
+
3
+ module DPL
4
+ class Provider
5
+ class GCS < Provider
6
+ requires 'gstore'
7
+ requires 'mime-types', version: '~> 2.0'
8
+
9
+ def needs_key?
10
+ false
11
+ end
12
+
13
+ def client
14
+ @client ||= GStore::Client.new(
15
+ :access_key => option(:access_key_id),
16
+ :secret_key => option(:secret_access_key)
17
+ )
18
+ end
19
+
20
+ def check_auth
21
+ log "Logging in with Access Key: #{option(:access_key_id)[-4..-1].rjust(20, '*')}"
22
+ end
23
+
24
+ def upload_path(filename)
25
+ [options[:upload_dir], filename].compact.join("/")
26
+ end
27
+
28
+ def push_app
29
+ glob_args = ["**/*"]
30
+ glob_args << File::FNM_DOTMATCH if options[:dot_match]
31
+ Dir.chdir(options.fetch(:local_dir, Dir.pwd)) do
32
+ Dir.glob(*glob_args) do |filename|
33
+ next if File.directory?(filename)
34
+ content_type = MIME::Types.type_for(filename).first.to_s
35
+ opts = { :"Content-Type" => content_type }.merge(encoding_option_for(filename))
36
+ opts["Cache-Control"] = options[:cache_control] if options[:cache_control]
37
+ opts["x-goog-acl"] = options[:acl] if options[:acl]
38
+
39
+ client.put_object(
40
+ option(:bucket),
41
+ upload_path(filename),
42
+ { :data => File.read(filename), :headers => opts }
43
+ )
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+ def encoding_option_for(path)
50
+ if detect_encoding? && encoding_for(path)
51
+ {"Content-Encoding" => encoding_for(path)}
52
+ else
53
+ {}
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ module DPL
2
+ class Provider
3
+ class Hackage < Provider
4
+ apt_get 'cabal', 'cabal-install'
5
+
6
+ def check_auth
7
+ unless option(:username) and option(:password)
8
+ raise Error, "must supply username and password"
9
+ end
10
+ end
11
+
12
+ def check_app
13
+ context.shell "cabal check" or raise Error, "cabal check failed"
14
+ end
15
+
16
+ def needs_key?
17
+ false
18
+ end
19
+
20
+ def push_app
21
+ context.shell "cabal sdist" or raise Error, "cabal sdist failed"
22
+ Dir.glob("dist/*.tar.gz") do |tar|
23
+ context.shell "cabal upload --username=#{option(:username)} --password=#{option(:password)} #{tar}"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,18 @@
1
+ module DPL
2
+ class Provider
3
+ module Heroku
4
+ autoload :API, 'dpl/provider/heroku/api'
5
+ autoload :Generic, 'dpl/provider/heroku/generic'
6
+ autoload :Git, 'dpl/provider/heroku/git'
7
+
8
+ extend self
9
+
10
+ def new(context, options)
11
+ strategy = options[:strategy] || 'api'
12
+ constant = constants.detect { |c| c.to_s.downcase == strategy.downcase.gsub(/\W/, '') }
13
+ raise Error, 'unknown strategy %p' % strategy unless constant and constant != Generic
14
+ const_get(constant).new(context, options)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,98 @@
1
+ require 'json'
2
+ require 'shellwords'
3
+ require 'logger'
4
+
5
+ module DPL
6
+ class Provider
7
+ module Heroku
8
+ class API < Generic
9
+ attr_reader :build_id
10
+ requires 'faraday'
11
+ requires 'rendezvous'
12
+
13
+ def push_app
14
+ pack_archive
15
+ upload_archive
16
+ trigger_build
17
+ verify_build
18
+ end
19
+
20
+ def archive_file
21
+ Shellwords.escape("#{context.env['HOME']}/.dpl.#{option(:app)}.tgz")
22
+ end
23
+
24
+ def pack_archive
25
+ log "creating application archive"
26
+ context.shell "tar -zcf #{archive_file} --exclude .git ."
27
+ end
28
+
29
+ def upload_archive
30
+ log "uploading application archive"
31
+ context.shell "curl #{Shellwords.escape(put_url)} -X PUT -H 'Content-Type:' -H 'Accept: application/vnd.heroku+json; version=3' --data-binary @#{archive_file}"
32
+ end
33
+
34
+ def trigger_build
35
+ log "triggering new deployment"
36
+ response = faraday.post("/apps/#{option(:app)}/builds") do |req|
37
+ req.headers['Content-Type'] = 'application/json'
38
+ req.body = {
39
+ "source_blob" => {
40
+ "url" => get_url,
41
+ "version" => version
42
+ }
43
+ }.to_json
44
+ end
45
+
46
+ if response.success?
47
+ @build_id = JSON.parse(response.body)['id']
48
+ output_stream_url = JSON.parse(response.body)['output_stream_url']
49
+ context.shell "curl #{Shellwords.escape(output_stream_url)} -H 'Accept: application/vnd.heroku+json; version=3'"
50
+ else
51
+ handle_error_response(response)
52
+ end
53
+ end
54
+
55
+ def verify_build
56
+ loop do
57
+ response = faraday.get("/apps/#{option(:app)}/builds/#{build_id}/result")
58
+ exit_code = JSON.parse(response.body)['exit_code']
59
+ if exit_code.nil?
60
+ log "heroku build still pending"
61
+ sleep 5
62
+ next
63
+ elsif exit_code == 0
64
+ break
65
+ else
66
+ error "deploy failed, build exited with code #{exit_code}"
67
+ end
68
+ end
69
+ end
70
+
71
+ def get_url
72
+ source_blob.fetch("get_url")
73
+ end
74
+
75
+ def put_url
76
+ source_blob.fetch("put_url")
77
+ end
78
+
79
+ def source_blob
80
+ return @source_blob if @source_blob
81
+
82
+ response = faraday.post('/sources')
83
+
84
+ if response.success?
85
+ @source_blob = JSON.parse(response.body)["source_blob"]
86
+ else
87
+ handle_error_response(response)
88
+ end
89
+ end
90
+
91
+ def version
92
+ @version ||= options[:version] || context.env['TRAVIS_COMMIT'] || `git rev-parse HEAD`.strip
93
+ end
94
+
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,94 @@
1
+ require 'json'
2
+
3
+ module DPL
4
+ class Provider
5
+ module Heroku
6
+ class Generic < Provider
7
+ requires 'rendezvous'
8
+ requires 'faraday'
9
+
10
+ attr_reader :app, :user
11
+
12
+ def needs_key?
13
+ false
14
+ end
15
+
16
+ def faraday
17
+ return @conn if @conn
18
+ headers = { "Accept" => "application/vnd.heroku+json; version=3" }
19
+
20
+ if options[:user] and options[:password]
21
+ # no-op
22
+ else
23
+ headers.merge!({ "Authorization" => "Bearer #{option(:api_key)}" })
24
+ end
25
+
26
+ @conn = Faraday.new( url: 'https://api.heroku.com', headers: headers ) do |faraday|
27
+ if options[:user] and options[:password]
28
+ faraday.basic_auth(options[:user], options[:password])
29
+ end
30
+ if log_level = options[:log_level]
31
+ logger = Logger.new($stderr)
32
+ logger.level = Logger.const_get(log_level.upcase)
33
+
34
+ faraday.response :logger, logger do | logger |
35
+ logger.filter(/(.*Authorization: ).*/,'\1[REDACTED]')
36
+ end
37
+ end
38
+ faraday.adapter Faraday.default_adapter
39
+ end
40
+ end
41
+
42
+ def check_auth
43
+ response = faraday.get('/account')
44
+
45
+ if response.success?
46
+ email = JSON.parse(response.body)["email"]
47
+ @user = email
48
+ log "authentication succeeded"
49
+ else
50
+ handle_error_response(response)
51
+ end
52
+ end
53
+
54
+ def handle_error_response(response)
55
+ error_response = JSON.parse(response.body)
56
+ error "API request failed.\nMessage: #{error_response["message"]}\nReference: #{error_response["url"]}"
57
+ end
58
+
59
+ def check_app
60
+ log "checking for app #{option(:app)}"
61
+ response = faraday.get("/apps/#{option(:app)}")
62
+ if response.success?
63
+ @app = JSON.parse(response.body)
64
+ log "found app #{@app["name"]}"
65
+ else
66
+ handle_error_response(response)
67
+ end
68
+ end
69
+
70
+ def restart
71
+ response = faraday.delete "/apps/#{option(:app)}/dynos" do |req|
72
+ req.headers['Content-Type'] = 'application/json'
73
+ end
74
+ unless response.success?
75
+ handle_error_response(response)
76
+ end
77
+ end
78
+
79
+ def run(command)
80
+ response = faraday.post "/apps/#{option(:app)}/dynos" do |req|
81
+ req.headers['Content-Type'] = 'application/json'
82
+ req.body = {"command" => command, "attach" => true}.to_json
83
+ end
84
+ if response.success?
85
+ rendezvous_url = JSON.parse(response.body)["attach_url"]
86
+ Rendezvous.start(url: rendezvous_url)
87
+ else
88
+ handle_error_response(response)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,28 @@
1
+ module DPL
2
+ class Provider
3
+ module Heroku
4
+ class Git < Generic
5
+ requires 'netrc'
6
+
7
+ def git_url
8
+ "https://git.heroku.com/#{option(:app)}.git"
9
+ end
10
+
11
+ def push_app
12
+ git_remote = options[:git] || git_url
13
+ write_netrc if git_remote.start_with?("https://")
14
+ log "$ git fetch origin $TRAVIS_BRANCH --unshallow"
15
+ context.shell "git fetch origin $TRAVIS_BRANCH --unshallow"
16
+ log "$ git push #{git_remote} HEAD:refs/heads/master -f"
17
+ context.shell "git push #{git_remote} HEAD:refs/heads/master -f"
18
+ end
19
+
20
+ def write_netrc
21
+ n = Netrc.read
22
+ n['git.heroku.com'] = [user, option(:api_key)]
23
+ n.save
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,236 @@
1
+ require 'json'
2
+ require 'tempfile'
3
+ require 'fileutils'
4
+
5
+ module DPL
6
+ class Provider
7
+ class Lambda < Provider
8
+ requires 'aws-sdk', version: '~> 2.0'
9
+ requires 'rubyzip', load: 'zip'
10
+
11
+ def lambda
12
+ @lambda ||= ::Aws::Lambda::Client.new(lambda_options)
13
+ end
14
+
15
+ def lambda_options
16
+ {
17
+ region: options[:region] || 'us-east-1',
18
+ credentials: ::Aws::Credentials.new(option(:access_key_id), option(:secret_access_key))
19
+ }
20
+ end
21
+
22
+ def push_app
23
+
24
+ # The original LambdaPreview client supported create/update in one call
25
+ # To keep compatibility we try to fetch the function and then decide
26
+ # whether to update the code or create a new function
27
+
28
+ function_name = options[:name] || option(:function_name)
29
+
30
+ begin
31
+ response = lambda.get_function({function_name: function_name})
32
+
33
+ log "Function #{function_name} already exists, updating."
34
+
35
+ # Options defined at
36
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/Lambda/Client.html#update_function_configuration-instance_method
37
+ response = lambda.update_function_configuration({
38
+ function_name: function_name,
39
+ description: options[:description] || default_description,
40
+ timeout: options[:timeout] || default_timeout,
41
+ memory_size: options[:memory_size] || default_memory_size,
42
+ role: option(:role),
43
+ handler: handler,
44
+ runtime: options[:runtime] || default_runtime,
45
+ vpc_config: vpc_config,
46
+ environment: environment_variables,
47
+ dead_letter_config: dead_letter_arn,
48
+ kms_key_arn: options[:kms_key_arn] || default_kms_key_arn,
49
+ tracing_config: tracing_mode
50
+ })
51
+
52
+ log "Updated configuration of function: #{response.function_name}."
53
+
54
+ if function_tags
55
+ log "Add tags to function #{response.function_name}."
56
+ response = lambda.tag_resource({
57
+ resource: response.function_arn,
58
+ tags: function_tags
59
+ })
60
+ end
61
+
62
+ # Options defined at
63
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/Lambda/Client.html#update_function_code-instance_method
64
+ response = lambda.update_function_code({
65
+ function_name: options[:name] || option(:function_name),
66
+ zip_file: function_zip,
67
+ publish: publish
68
+ })
69
+
70
+ log "Updated code of function: #{response.function_name}."
71
+ rescue ::Aws::Lambda::Errors::ResourceNotFoundException
72
+ log "Function #{function_name} does not exist, creating."
73
+ # Options defined at
74
+ # https://docs.aws.amazon.com/lambda/latest/dg/API_CreateFunction.html
75
+ response = lambda.create_function({
76
+ function_name: options[:name] || option(:function_name),
77
+ description: options[:description] || default_description,
78
+ timeout: options[:timeout] || default_timeout,
79
+ memory_size: options[:memory_size] || default_memory_size,
80
+ role: option(:role),
81
+ handler: handler,
82
+ code: {
83
+ zip_file: function_zip,
84
+ },
85
+ runtime: options[:runtime] || default_runtime,
86
+ publish: publish,
87
+ vpc_config: vpc_config,
88
+ environment: environment_variables,
89
+ dead_letter_config: dead_letter_arn,
90
+ kms_key_arn: options[:kms_key_arn] || default_kms_key_arn,
91
+ tracing_config: tracing_mode,
92
+ tags: function_tags
93
+ })
94
+
95
+ log "Created lambda: #{response.function_name}."
96
+ end
97
+ rescue ::Aws::Lambda::Errors::ServiceException => exception
98
+ error(exception.message)
99
+ rescue ::Aws::Lambda::Errors::InvalidParameterValueException => exception
100
+ error(exception.message)
101
+ rescue ::Aws::Lambda::Errors::ResourceNotFoundException => exception
102
+ error(exception.message)
103
+ end
104
+
105
+ def handler
106
+ module_name = options[:module_name] || default_module_name
107
+ handler_name = option(:handler_name)
108
+
109
+ "#{module_name}.#{handler_name}"
110
+ end
111
+
112
+ def function_zip
113
+ target_zip_path = File.absolute_path(options[:zip] || Dir.pwd)
114
+ dest_file_path = output_file_path
115
+
116
+ if File.directory?(target_zip_path)
117
+ zip_directory(dest_file_path, target_zip_path)
118
+ elsif File.file?(target_zip_path)
119
+ zip_file(dest_file_path, target_zip_path)
120
+ else
121
+ error('Invalid zip option. If set, must be path to directory, js file, or a zip file.')
122
+ end
123
+
124
+ File.new(dest_file_path)
125
+ end
126
+
127
+ def zip_file(dest_file_path, target_file_path)
128
+ if File.extname(target_file_path) == '.zip'
129
+ # Just copy it to the destination right away, since it is already a zip.
130
+ FileUtils.cp(target_file_path, dest_file_path)
131
+ dest_file_path
132
+ else
133
+ # Zip up the file.
134
+ src_directory_path = File.dirname(target_file_path)
135
+ files = [ target_file_path ]
136
+
137
+ create_zip(dest_file_path, src_directory_path, files)
138
+ end
139
+ end
140
+
141
+ def zip_directory(dest_file_path, target_directory_path)
142
+ files = Dir[File.join(target_directory_path, '**', '**')]
143
+ create_zip(dest_file_path, target_directory_path, files)
144
+ end
145
+
146
+ def create_zip(dest_file_path, src_directory_path, files)
147
+ Zip::File.open(dest_file_path, Zip::File::CREATE) do |zipfile|
148
+ files.each do |file|
149
+ zipfile.add(file.sub(src_directory_path + File::SEPARATOR, ''), file)
150
+ end
151
+ end
152
+
153
+ dest_file_path
154
+ end
155
+
156
+ def needs_key?
157
+ false
158
+ end
159
+
160
+ def check_auth
161
+ log "Using Access Key: #{option(:access_key_id)[-4..-1].rjust(20, '*')}"
162
+ end
163
+
164
+ def output_file_path
165
+ @output_file_path ||= '/tmp/' + random_chars(8) + '-lambda.zip'
166
+ end
167
+
168
+ def vpc_config
169
+ options[:subnet_ids] && options[:security_group_ids] ? { :subnet_ids => Array(options[:subnet_ids]), :security_group_ids => Array(options[:security_group_ids]) } : nil
170
+ end
171
+
172
+ def environment_variables
173
+ options[:environment_variables] ? { :variables => split_string_array_to_hash(options[:environment_variables]) } : nil
174
+ end
175
+
176
+ def dead_letter_arn
177
+ options[:dead_letter_arn] ? { :target_arn => options[:dead_letter_arn]} : nil
178
+ end
179
+
180
+ def tracing_mode
181
+ options[:tracing_mode] ? { :mode => options[:tracing_mode]} : nil
182
+ end
183
+
184
+ def default_kms_key_arn
185
+ nil
186
+ end
187
+
188
+ def function_tags
189
+ options[:function_tags] ? split_string_array_to_hash(options[:function_tags]) : nil
190
+ end
191
+
192
+ def default_runtime
193
+ 'nodejs'
194
+ end
195
+
196
+ def default_timeout
197
+ 3 # seconds
198
+ end
199
+
200
+ def default_description
201
+ "Deploy build #{context.env['TRAVIS_BUILD_NUMBER']} to AWS Lambda via Travis CI"
202
+ end
203
+
204
+ def default_memory_size
205
+ 128
206
+ end
207
+
208
+ def default_module_name
209
+ 'index'
210
+ end
211
+
212
+ def publish
213
+ !!options[:publish]
214
+ end
215
+
216
+ def split_string_array_to_hash(arr, delimiter="=")
217
+ variables = {}
218
+ Array(arr).map do |val|
219
+ keyval = val.split(delimiter)
220
+ variables[keyval[0]] = keyval[1]
221
+ end
222
+ variables
223
+ end
224
+
225
+ def random_chars(count=8)
226
+ (36**(count-1) + rand(36**count - 36**(count-1))).to_s(36)
227
+ end
228
+
229
+ def cleanup
230
+ end
231
+
232
+ def uncleanup
233
+ end
234
+ end
235
+ end
236
+ end