bookbindery 4.1.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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