bookbindery 4.1.0 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bookbinder/cf_command_runner.rb +33 -30
- data/lib/bookbinder/cli.rb +15 -7
- data/lib/bookbinder/code_example_reader.rb +5 -7
- data/lib/bookbinder/commands/bind/directory_preparer.rb +1 -1
- data/lib/bookbinder/commands/build_and_push_tarball.rb +18 -6
- data/lib/bookbinder/commands/collection.rb +16 -21
- data/lib/bookbinder/commands/push_from_local.rb +27 -19
- data/lib/bookbinder/commands/push_to_prod.rb +30 -34
- data/lib/bookbinder/commands/tag.rb +5 -6
- data/lib/bookbinder/commands/update_local_doc_repos.rb +0 -1
- data/lib/bookbinder/config/cf_credentials.rb +1 -14
- data/lib/bookbinder/deploy/app_fetcher.rb +36 -0
- data/lib/bookbinder/deploy/archive.rb +102 -0
- data/lib/bookbinder/deploy/artifact.rb +26 -0
- data/lib/bookbinder/deploy/blue_green_app.rb +29 -0
- data/lib/bookbinder/deploy/cf_routes.rb +32 -0
- data/lib/bookbinder/deploy/deployment.rb +55 -0
- data/lib/bookbinder/deploy/distributor.rb +76 -0
- data/lib/bookbinder/deploy/failure.rb +15 -0
- data/lib/bookbinder/deploy/pusher.rb +34 -0
- data/lib/bookbinder/deploy/success.rb +16 -0
- data/lib/bookbinder/ingest/cloner_factory.rb +4 -4
- data/lib/bookbinder/ingest/local_filesystem_cloner.rb +5 -6
- data/lib/bookbinder/local_file_system_accessor.rb +9 -0
- data/lib/bookbinder/{post_production → postprocessing}/sitemap_writer.rb +7 -2
- data/lib/bookbinder/preprocessing/{copy_to_site_gen_dir.rb → link_to_site_gen_dir.rb} +2 -2
- data/lib/bookbinder/server_director.rb +3 -10
- data/lib/bookbinder/sheller.rb +5 -1
- data/master_middleman/bookbinder_helpers.rb +4 -12
- data/template_app/config.ru +3 -7
- data/template_app/rack_app.rb +23 -0
- metadata +60 -58
- data/lib/bookbinder/app_fetcher.rb +0 -36
- data/lib/bookbinder/archive.rb +0 -102
- data/lib/bookbinder/artifact_namer.rb +0 -22
- data/lib/bookbinder/commands/bookbinder_command.rb +0 -18
- data/lib/bookbinder/distributor.rb +0 -80
- data/lib/bookbinder/pusher.rb +0 -34
- data/lib/bookbinder/time_fetcher.rb +0 -7
- data/lib/bookbinder/values/blue_green_app.rb +0 -27
- 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
|
data/lib/bookbinder/archive.rb
DELETED
@@ -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
|
data/lib/bookbinder/pusher.rb
DELETED
@@ -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,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
|