Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve logging on failed container deletion #32

Merged
merged 1 commit into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions lib/fpm/fry/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ class ContainerCreationFailed < StandardError
include FPM::Fry::WithData
end

class ContainerDeletionFailed < StandardError
include FPM::Fry::WithData
end

# Raised when trying to read file that can't be read e.g. because it's a
# directory.
class NotAFile < StandardError
Expand Down Expand Up @@ -111,7 +115,7 @@ def read(name, resource)
)
end
rescue Excon::Error => e
@logger.error("unexpected response when reading resource: url: #{url}, error: #{e}")
logger.error("unexpected response when reading resource: url: #{url}, error: #{e}")
raise
end
if [404,500].include? res.status
Expand Down Expand Up @@ -165,14 +169,14 @@ def link_target(name, resource)
end

def copy(name, resource, map, options = {})
ex = FPM::Fry::Tar::Extractor.new(logger: @logger)
ex = FPM::Fry::Tar::Extractor.new(logger: logger)
base = File.dirname(resource)
read(name, resource) do | entry |
file = File.join(base, entry.full_name).chomp('/')
file = file.sub(%r"\A\./",'')
to = map[file]
next unless to
@logger.debug("Copy",name: file, to: to)
logger.debug("Copy",name: file, to: to)
ex.extract_entry(to, entry, options)
end
end
Expand All @@ -182,7 +186,7 @@ def changes(name)
res = agent.get(path: url, expects: [200, 204])
return JSON.parse(res.body)
rescue Excon::Error => e
@logger.error("could not retrieve changes for: #{name}, url: #{url}, error: #{e}")
logger.error("could not retrieve changes for: #{name}, url: #{url}, error: #{e}")
raise
end

Expand Down Expand Up @@ -220,27 +224,35 @@ def create(image)
)
data = JSON.parse(res.body)
if res.status != 201
@logger.error(data["message"])
logger.error(data["message"])
if res.status == 404
@logger.info("execute docker pull #{image} first or specify --pull argument for fpm-fry")
logger.info("execute docker pull #{image} first or specify --pull argument for fpm-fry")
end
raise ContainerCreationFailed.new("could not create container from #{image}", message: data["message"])
end
data['Id']
rescue Excon::Error => e
@logger.error("could not create container from #{image}, url: #{url}, error: #{e}")
logger.error("could not create container from #{image}, url: #{url}, error: #{e}")
raise
end

def destroy(container)
return unless container

url = self.url('containers', container)
agent.delete(
res = agent.delete(
path: url,
expects: [204]
expects: [204, 409]
)
return unless res.status == 409
data = JSON.parse(res.body) rescue ({"message" => "could not parse response body: '#{res.body}'"})
if data["message"] =~ /removal of container .* is already in progress/
logger.info(data["message"])
else
raise ContainerDeletionFailed.new("could not destroy container #{container}", data)
end
rescue Excon::Error => e
@logger.error("could not destroy container: #{container}, url: #{url}, error: #{e}")
logger.error("could not destroy container: #{container}, url: #{url}, error: #{e}")
raise
end

Expand Down
144 changes: 75 additions & 69 deletions lib/fpm/fry/command/cook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
module FPM; module Fry
class Command::Cook < Command

class BuildFailed < StandardError
include FPM::Fry::WithData
end

option '--keep', :flag, 'Keep the container after build'
option '--overwrite', :flag, 'Overwrite package', default: true
option '--verbose', :flag, 'Verbose output', default: false
Expand Down Expand Up @@ -212,79 +216,81 @@ def pull_base_image!
end

def build!
body = begin
url = client.url('containers','create')
args = {
headers: {
'Content-Type' => 'application/json'
},
path: url,
expects: [201],
body: JSON.generate({"Image" => build_image})
}
args[:query] = { platform: platform } if platform
res = client.post(args)
JSON.parse(res.body)
rescue Excon::Error
logger.error "could not create #{build_image}, url: #{url}"
raise
end
container = body['Id']
begin
begin
url = client.url('containers',container,'start')
client.post(
headers: {
'Content-Type' => 'application/json'
},
path: url,
expects: [204],
body: JSON.generate({})
)
rescue Excon::Error
logger.error "could not start container #{container}, url: #{url}"
raise
end
container = create_build_container
start_build_container(container)
attach_to_build_container_and_stream_logs(container)
wait_for_build_container_to_shut_down(container)
yield container
ensure
unless keep?
client.destroy(container) if container
end
end

begin
url = client.url('containers',container,'attach?stderr=1&stdout=1&stream=1&logs=1')
client.post(
path: url,
body: '',
expects: [200],
middlewares: [
StreamParser.new(out,err),
Excon::Middleware::Expects,
Excon::Middleware::Instrumentor,
Excon::Middleware::Mock
]
)
rescue Excon::Error
logger.error "could not attach to container #{container}, url: #{url}"
raise
end
def create_build_container
url = client.url('containers','create')
args = {
headers: {
'Content-Type' => 'application/json'
},
path: url,
expects: [201],
body: JSON.generate({"Image" => build_image})
}
args[:query] = { platform: platform } if platform
res = client.post(args)
JSON.parse(res.body)['Id']
rescue Excon::Error
logger.error "could not create #{build_image}, url: #{url}"
raise
end

begin
res = client.post(
path: client.url('containers',container,'wait'),
expects: [200],
body: ''
)
json = JSON.parse(res.body)
if json["StatusCode"] != 0
raise Fry::WithData("Build failed", exit_code: json["StatusCode"])
end
rescue Excon::Error
logger.error "could not wait successfully for container #{container}, url: #{url}"
raise
end
def start_build_container(container)
url = client.url('containers',container,'start')
client.post(
headers: {
'Content-Type' => 'application/json'
},
path: url,
expects: [204],
body: JSON.generate({})
)
rescue Excon::Error
logger.error "could not start container #{container}, url: #{url}"
raise
end

yield container
ensure
unless keep?
client.destroy(container)
end
def attach_to_build_container_and_stream_logs(container)
url = client.url('containers',container,'attach?stderr=1&stdout=1&stream=1&logs=1')
client.post(
path: url,
body: '',
expects: [200],
middlewares: [
StreamParser.new(out,err),
Excon::Middleware::Expects,
Excon::Middleware::Instrumentor,
Excon::Middleware::Mock
]
)
rescue Excon::Error
logger.error "could not attach to container #{container}, url: #{url}"
raise
end

def wait_for_build_container_to_shut_down(container)
res = client.post(
path: client.url('containers',container,'wait'),
expects: [200],
body: ''
)
json = JSON.parse(res.body)
if json["StatusCode"] != 0
raise BuildFailed.new("Build script failed with non zero exit code", json)
end
rescue Excon::Error
logger.error "could not wait successfully for container #{container}, url: #{url}"
raise
end

def input_package(container)
Expand Down
Loading