bookbindery 4.1.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bookbinder/cf_command_runner.rb +33 -30
  3. data/lib/bookbinder/cli.rb +15 -7
  4. data/lib/bookbinder/code_example_reader.rb +5 -7
  5. data/lib/bookbinder/commands/bind/directory_preparer.rb +1 -1
  6. data/lib/bookbinder/commands/build_and_push_tarball.rb +18 -6
  7. data/lib/bookbinder/commands/collection.rb +16 -21
  8. data/lib/bookbinder/commands/push_from_local.rb +27 -19
  9. data/lib/bookbinder/commands/push_to_prod.rb +30 -34
  10. data/lib/bookbinder/commands/tag.rb +5 -6
  11. data/lib/bookbinder/commands/update_local_doc_repos.rb +0 -1
  12. data/lib/bookbinder/config/cf_credentials.rb +1 -14
  13. data/lib/bookbinder/deploy/app_fetcher.rb +36 -0
  14. data/lib/bookbinder/deploy/archive.rb +102 -0
  15. data/lib/bookbinder/deploy/artifact.rb +26 -0
  16. data/lib/bookbinder/deploy/blue_green_app.rb +29 -0
  17. data/lib/bookbinder/deploy/cf_routes.rb +32 -0
  18. data/lib/bookbinder/deploy/deployment.rb +55 -0
  19. data/lib/bookbinder/deploy/distributor.rb +76 -0
  20. data/lib/bookbinder/deploy/failure.rb +15 -0
  21. data/lib/bookbinder/deploy/pusher.rb +34 -0
  22. data/lib/bookbinder/deploy/success.rb +16 -0
  23. data/lib/bookbinder/ingest/cloner_factory.rb +4 -4
  24. data/lib/bookbinder/ingest/local_filesystem_cloner.rb +5 -6
  25. data/lib/bookbinder/local_file_system_accessor.rb +9 -0
  26. data/lib/bookbinder/{post_production → postprocessing}/sitemap_writer.rb +7 -2
  27. data/lib/bookbinder/preprocessing/{copy_to_site_gen_dir.rb → link_to_site_gen_dir.rb} +2 -2
  28. data/lib/bookbinder/server_director.rb +3 -10
  29. data/lib/bookbinder/sheller.rb +5 -1
  30. data/master_middleman/bookbinder_helpers.rb +4 -12
  31. data/template_app/config.ru +3 -7
  32. data/template_app/rack_app.rb +23 -0
  33. metadata +60 -58
  34. data/lib/bookbinder/app_fetcher.rb +0 -36
  35. data/lib/bookbinder/archive.rb +0 -102
  36. data/lib/bookbinder/artifact_namer.rb +0 -22
  37. data/lib/bookbinder/commands/bookbinder_command.rb +0 -18
  38. data/lib/bookbinder/distributor.rb +0 -80
  39. data/lib/bookbinder/pusher.rb +0 -34
  40. data/lib/bookbinder/time_fetcher.rb +0 -7
  41. data/lib/bookbinder/values/blue_green_app.rb +0 -27
  42. data/lib/bookbinder/values/cf_routes.rb +0 -30
@@ -1,36 +0,0 @@
1
- require_relative 'values/blue_green_app'
2
- require_relative 'values/cf_routes'
3
-
4
- module Bookbinder
5
-
6
- class AppFetcher
7
- def initialize(routes_to_search, cf_command_runner)
8
- @routes_to_search = routes_to_search
9
- @cf_command_runner = cf_command_runner
10
- end
11
-
12
- def fetch_current_app
13
- raw_cf_routes = cf_command_runner.cf_routes_output
14
- cf_routes = CfRoutes.new(raw_cf_routes)
15
-
16
- existing_hosts = routes_to_search.select do |domain, host|
17
- cf_routes.apps_by_host_and_domain.has_key?([host, domain])
18
- end
19
-
20
- return nil if existing_hosts.empty?
21
- app_groups = existing_hosts.map { |domain, host| apps_for_host(cf_routes, domain, host) }
22
- apps_for_existing_routes = app_groups.first
23
- apps_for_existing_routes.first
24
- end
25
-
26
- private
27
-
28
- attr_reader :routes_to_search, :cf_command_runner
29
-
30
- def apps_for_host(routes_from_cf, domain, host)
31
- routes_from_cf.apps_by_host_and_domain.fetch([host, domain], []).
32
- map &BlueGreenApp.method(:new)
33
- end
34
- end
35
-
36
- end
@@ -1,102 +0,0 @@
1
- require 'fog/aws'
2
- require 'tmpdir'
3
- require_relative 'artifact_namer'
4
- require_relative 'deprecated_logger'
5
- require_relative 'time_fetcher'
6
-
7
- module Bookbinder
8
- class Archive
9
- class FileDoesNotExist < StandardError; end
10
- class NoNamespaceGiven < StandardError; end
11
-
12
- def initialize(logger: nil, time_fetcher: TimeFetcher.new, key: '', secret: '')
13
- @logger = logger
14
- @time_fetcher = time_fetcher
15
- @aws_key = key
16
- @aws_secret_key = secret
17
- end
18
-
19
- def create_and_upload_tarball(build_number: nil, app_dir: 'final_app', bucket: '', namespace: nil)
20
- build_number ||= time_fetcher.current_time
21
- raise 'You must provide a namespace to push an identifiable build.' unless namespace
22
-
23
- tarball_filename, tarball_path = create_tarball(app_dir, build_number, namespace)
24
-
25
- upload_file(bucket, tarball_filename, tarball_path)
26
- @logger.log "Green build ##{build_number.to_s.green} has been uploaded to S3 for #{namespace.to_s.cyan}"
27
- end
28
-
29
- def upload_file(bucket, name, source_path)
30
- find_or_create_directory(bucket).
31
- files.create(key: name,
32
- body: File.read(source_path),
33
- public: true)
34
- end
35
-
36
- def download(download_dir: nil, bucket: nil, build_number: nil, namespace: nil)
37
- raise NoNamespaceGiven, 'One must specify a namespace to find files in this bucket' unless namespace
38
-
39
- directory = connection.directories.get bucket
40
- build_number ||= latest_build_number_for_namespace(directory, namespace)
41
- filename = ArtifactNamer.new(namespace, build_number, 'tgz').filename
42
-
43
- s3_file = directory.files.get(filename)
44
- raise FileDoesNotExist, "Unable to find tarball on AWS for book '#{namespace}', build number: #{build_number}" unless s3_file
45
-
46
- downloaded_file = File.join(Dir.mktmpdir, 'downloaded.tgz')
47
- File.open(downloaded_file, 'wb') { |f| f.write(s3_file.body) }
48
- Dir.chdir(download_dir) { `tar xzf #{downloaded_file}` }
49
-
50
- @logger.log "Green build ##{build_number.to_s.green} has been downloaded from S3 and untarred into #{download_dir.to_s.cyan}"
51
- end
52
-
53
- def tarball_name_regex(namespace)
54
- /^#{namespace}-(\d+_?\d*)\.tgz/
55
- end
56
-
57
- private
58
-
59
- attr_reader :time_fetcher
60
-
61
- def find_or_create_directory(name)
62
- connection.directories.create(key: name)
63
- rescue Excon::Errors::Conflict
64
- connection.directories.get(name)
65
- end
66
-
67
- def create_tarball(app_dir, build_number, namespace)
68
- tarball_filename = ArtifactNamer.new(namespace, build_number, 'tgz').filename
69
- tarball_path = File.join(Dir.mktmpdir, tarball_filename)
70
-
71
- Dir.chdir(app_dir) { `tar czf #{tarball_path} *` }
72
- return tarball_filename, tarball_path
73
- end
74
-
75
- def latest_build_number_for_namespace(directory, namespace)
76
- all_files = all_files_workaround_for_fog_map_limitation(directory.files)
77
-
78
- all_files_with_namespace = all_files.map do |file|
79
- filename = file.key
80
- file if filename[tarball_name_regex(namespace)]
81
- end.compact
82
-
83
- return nil if all_files_with_namespace.empty?
84
- most_recent_file = all_files_with_namespace.sort_by { |file| file.last_modified }.last
85
-
86
- most_recent_filename = most_recent_file.key
87
- most_recent_filename[tarball_name_regex(namespace), 1]
88
- end
89
-
90
- def connection
91
- @connection ||= Fog::Storage.new :provider => 'AWS',
92
- :aws_access_key_id => @aws_key,
93
- :aws_secret_access_key => @aws_secret_key
94
- end
95
-
96
- def all_files_workaround_for_fog_map_limitation(files)
97
- all_files = []
98
- files.each { |f| all_files << f }
99
- all_files
100
- end
101
- end
102
- end
@@ -1,22 +0,0 @@
1
- module Bookbinder
2
- class ArtifactNamer
3
- def initialize(namespace, build_number, extension, path = '.')
4
- @namespace = namespace
5
- @build_number = build_number
6
- @path = path
7
- @extension = extension
8
- end
9
-
10
- def full_path
11
- File.join(path, filename)
12
- end
13
-
14
- def filename
15
- "#{namespace}-#{build_number}.#{extension}"
16
- end
17
-
18
- private
19
-
20
- attr_reader :namespace, :build_number, :path, :extension
21
- end
22
- end
@@ -1,18 +0,0 @@
1
- module Bookbinder
2
- module Commands
3
- class BookbinderCommand
4
- def initialize(logger, configuration_fetcher)
5
- @logger = logger
6
- @configuration_fetcher = configuration_fetcher
7
- end
8
-
9
- private
10
-
11
- def config
12
- @config ||= configuration_fetcher.fetch_config
13
- end
14
-
15
- attr_reader :configuration_fetcher
16
- end
17
- end
18
- end
@@ -1,80 +0,0 @@
1
- require_relative 'app_fetcher'
2
- require_relative 'archive'
3
- require_relative 'artifact_namer'
4
- require_relative 'cf_command_runner'
5
- require_relative 'ingest/destination_directory'
6
- require_relative 'pusher'
7
- require_relative 'sheller'
8
-
9
- module Bookbinder
10
- class Distributor
11
- EXPIRATION_HOURS = 2
12
-
13
- def self.build(logger, options)
14
- namespace = Ingest::DestinationDirectory.new(options[:book_repo])
15
- namer = ArtifactNamer.new(namespace, options[:build_number], 'log', '/tmp')
16
-
17
- archive = Archive.new(logger: logger, key: options[:aws_credentials].access_key, secret: options[:aws_credentials].secret_key)
18
- cf_command_runner = CfCommandRunner.new(logger, Sheller.new, options[:cf_credentials], namer.full_path)
19
- cf_app_fetcher = AppFetcher.new(options[:cf_credentials].flat_routes, cf_command_runner)
20
-
21
- pusher = Pusher.new(cf_command_runner, cf_app_fetcher)
22
- new(logger, archive, pusher, namespace, namer, options)
23
- end
24
-
25
- def initialize(logger, archive, pusher, namespace, namer, options)
26
- @logger = logger
27
- @archive = archive
28
- @pusher = pusher
29
- @namespace = namespace
30
- @namer = namer
31
- @options = options
32
- end
33
-
34
- def distribute
35
- download if cf_credentials.download_archive_before_push?
36
- push_app
37
- nil
38
- rescue => e
39
- @logger.error(<<-ERROR.chomp)
40
- [ERROR] #{e.message}
41
- [DEBUG INFO]
42
- CF organization: #{cf_credentials.organization}
43
- CF space: #{cf_credentials.space}
44
- CF account: #{cf_credentials.username}
45
- routes: #{cf_credentials.routes}
46
- ERROR
47
- raise
48
- ensure
49
- upload_trace
50
- end
51
-
52
- private
53
-
54
- attr_reader :options, :archive, :namer, :namespace, :pusher
55
-
56
- def download
57
- archive.download(download_dir: options[:app_dir],
58
- bucket: options[:aws_credentials].green_builds_bucket,
59
- build_number: options[:build_number],
60
- namespace: namespace)
61
- end
62
-
63
- def push_app
64
- @logger.warn(cf_credentials.push_warning) if cf_credentials.push_warning
65
- pusher.push(options[:app_dir])
66
- end
67
-
68
- def upload_trace
69
- uploaded_file = archive.upload_file(options[:aws_credentials].green_builds_bucket, namer.filename, namer.full_path)
70
- @logger.log("Your cf trace file is available at: #{uploaded_file.url(Time.now.to_i + EXPIRATION_HOURS*60*60).green}")
71
- @logger.log("This URL will expire in #{EXPIRATION_HOURS} hours, so if you need to share it, make sure to save a copy now.")
72
- rescue Errno::ENOENT
73
- @logger.error "Could not find CF trace file: #{namer.full_path}"
74
- end
75
-
76
- def cf_credentials
77
- options[:cf_credentials]
78
- end
79
- end
80
- end
@@ -1,34 +0,0 @@
1
- require_relative 'app_fetcher'
2
-
3
- module Bookbinder
4
- class Pusher
5
- def initialize(cf_cli, app_fetcher)
6
- @cf_cli = cf_cli
7
- @app_fetcher = app_fetcher
8
- end
9
-
10
- def push(app_dir)
11
- Dir.chdir(app_dir) do
12
- cf_cli.login
13
-
14
- old_app = app_fetcher.fetch_current_app
15
-
16
- if old_app
17
- new_app = old_app.with_flipped_name
18
- cf_cli.start(new_app)
19
- cf_cli.push(new_app)
20
- cf_cli.map_routes(new_app)
21
- cf_cli.takedown_old_target_app(old_app)
22
- else
23
- new_app = cf_cli.new_app
24
- cf_cli.push(new_app)
25
- cf_cli.map_routes(new_app)
26
- end
27
- end
28
- end
29
-
30
- private
31
-
32
- attr_reader :cf_cli, :app_fetcher
33
- end
34
- end
@@ -1,7 +0,0 @@
1
- module Bookbinder
2
- class TimeFetcher
3
- def current_time
4
- Time.now.strftime("%Y%m%d_%H%M")
5
- end
6
- end
7
- end
@@ -1,27 +0,0 @@
1
- module Bookbinder
2
- class BlueGreenApp
3
- def initialize(name)
4
- @name = name.strip
5
- end
6
-
7
- def ==(other)
8
- to_s == other.to_s
9
- end
10
-
11
- def to_s
12
- name
13
- end
14
-
15
- def with_flipped_name
16
- if name.match(/green$/)
17
- BlueGreenApp.new(name.sub(/green$/, 'blue'))
18
- else
19
- BlueGreenApp.new(name.sub(/blue$/, 'green'))
20
- end
21
- end
22
-
23
- private
24
-
25
- attr_reader :name
26
- end
27
- end
@@ -1,30 +0,0 @@
1
- module Bookbinder
2
- class CfRoutes
3
- def initialize(raw_routes)
4
- @raw_routes = raw_routes
5
- end
6
-
7
- def apps_by_host_and_domain
8
- @apps_by_host_and_domain ||= data(raw_routes).reduce({}) {|acc, row|
9
- parsed_row = Hash[headers(raw_routes).zip(row)]
10
- acc.merge(parsed_row.values_at('host', 'domain') => parse_apps(parsed_row['apps']))
11
- }
12
- end
13
-
14
- private
15
-
16
- attr_reader :raw_routes
17
-
18
- def parse_apps(apps)
19
- apps.split(',').map(&:strip)
20
- end
21
-
22
- def headers(raw)
23
- raw.lines[2].split(/\s+/)
24
- end
25
-
26
- def data(raw)
27
- raw.lines[3..-1].map {|line| line.split(/\s+/, headers(raw).size)}
28
- end
29
- end
30
- end