Class: Visor::Image::PostImage

Inherits:
Goliath::API
  • Object
show all
Includes:
Common::Exception, Common::Util
Defined in:
lib/image/routes/post_image.rb

Overview

Post image data and metadata and returns the registered metadata.

Instance Method Summary collapse

Instance Method Details

#do_upload(id, meta, body) ⇒ Array

Upload image file to wanted store.

Parameters:

  • id (Fixnum)

    The image _id.

  • meta (Hash)

    The image metadata.

  • body (FIle)

    The image body tempfile descriptor.

Returns:

  • (Array)

    Image file location URI and size.

Raises:

  • (ArgumentError)

    If request Content-Type isn’t ‘application/octet-stream’



173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/image/routes/post_image.rb', line 173

def do_upload(id, meta, body)
  content_type = env['headers']['Content-Type'] || ''
  store_name   = meta[:store] || configs[:default]
  format       = meta[:format] || 'none'

  unless content_type == 'application/octet-stream'
    raise ArgumentError, 'Request Content-Type must be application/octet-stream'
  end

  store = Visor::Image::Store.get_backend(store_name, configs)
  logger.debug "Uploading image #{id} data to #{store_name} store"
  store.save(id, body, format)
end

#exit_error(code, message, set_status = false) ⇒ Array

Produce an HTTP response with an error code and message.

Parameters:

  • code (Fixnum)

    The error code.

  • message (String)

    The error message.

  • set_status (True, False) (defaults to: false)

    (false) If true, update the image status to ‘error’.

Returns:

  • (Array)

    The HTTP response containing an error code and its message.



113
114
115
116
117
118
119
120
121
# File 'lib/image/routes/post_image.rb', line 113

def exit_error(code, message, set_status=false)
  logger.error message
  begin
    vms.put_image(env['id'], status: 'error') if set_status
  rescue => e
    logger.error "Unable to set image #{env['id']} status to 'error': #{e.message}"
  end
  [code, {}, {code: code, message: message}]
end

#insert_meta(meta) ⇒ Hash

Insert image metadata on database (which set its status to locked).

Parameters:

  • meta (Hash)

    The image metadata.

Returns:

  • (Hash)

    The already inserted image metadata.



129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/image/routes/post_image.rb', line 129

def insert_meta(meta)
  image     = vms.post_image(meta)
  env['id'] = image[:_id]

  if image[:location]
    logger.debug "Location for image #{env['id']} is #{image[:location]}"
    logger.debug "Setting image #{env['id']} status to 'available'"
    vms.put_image(env['id'], status: 'available')
  else
    image
  end
end

#on_body(env, data) ⇒ Object

Pre-process body as it arrives in streaming chunks and load them into a tempfile.

Parameters:

  • env (Object)

    The Goliath environment variables.

  • data (Object)

    The incoming request HTTP body chunks.



29
30
31
32
# File 'lib/image/routes/post_image.rb', line 29

def on_body(env, data)
  (env['body'] ||= Tempfile.open('visor-image', encoding: 'ascii-8bit')) << data
  (env['md5'] ||= Digest::MD5.new) << data
end

#on_close(env) ⇒ Object

On connection close log a message.

Parameters:

  • env (Object)

    The Goliath environment variables.



101
102
103
# File 'lib/image/routes/post_image.rb', line 101

def on_close(env)
  logger.info 'Connection closed'
end

#on_headers(env, headers) ⇒ Object

Pre-process headers as they arrive and load them into a environment variable.

Parameters:

  • env (Object)

    The Goliath environment variables.

  • headers (Object)

    The incoming request HTTP headers.



19
20
21
22
# File 'lib/image/routes/post_image.rb', line 19

def on_headers(env, headers)
  logger.debug "Received headers: #{headers.inspect}"
  env['headers'] = headers
end

#response(env) ⇒ Array

Main response method which processes the received headers and body, managing image metadata and file data.

Parameters:

  • env (Object)

    The Goliath environment variables.

Returns:

  • (Array)

    The HTTP response containing the already inserted image metadata or an error code and its message if anything was raised.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/image/routes/post_image.rb', line 42

def response(env)
  begin
    access_key = authorize(env, vas)
  rescue Forbidden => e
    return exit_error(403, e.message)
  end

  meta         = pull_meta_from_headers(env['headers'])
  meta[:owner] = access_key
  body         = env['body']
  location     = meta[:location]

  if location && body
    msg = 'When the location header is present no file content can be provided'
    return exit_error(400, msg)
  end

  if meta[:store] == 'http' || (location && location.split(':').first == 'http')
    return exit_error(400, 'Cannot post an image file to a HTTP backend') if body
    store = Visor::Image::Store::HTTP.new(location)

    exist, meta[:size], meta[:checksum] = store.file_exists?(false)
    return exit_error(404, "No image file found at #{location}") unless exist
  end

  # first registers the image meta or raises on error
  begin
    image = insert_meta(meta)
  rescue ArgumentError => e
    body.close if body
    body.unlink if body
    return exit_error(400, e.message)
  rescue InternalError => e
    body.close if body
    body.unlink if body
    return exit_error(500, e.message)
  end

  # if has body(image file), upload file and update meta or raise on error
  begin
    image = upload_and_update(env['id'], body)
  rescue UnsupportedStore, ArgumentError => e
    return exit_error(400, e.message, true)
  rescue NotFound => e
    return exit_error(404, e.message, true)
  rescue Duplicated => e
    return exit_error(409, e.message, true)
  ensure
    body.close
    body.unlink
  end unless body.nil?

  [200, {}, {image: image}]
end

#upload_and_update(id, body) ⇒ Hash

Update image status and launch upload.

Parameters:

  • id (Fixnum)

    The image _id.

  • body (FIle)

    The image body tempfile descriptor.

Returns:

  • (Hash)

    The already updated image metadata.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/image/routes/post_image.rb', line 149

def upload_and_update(id, body)
  logger.debug "Setting image #{id} status to 'uploading'"
  meta           = vms.put_image(id, status: 'uploading')
  checksum       = env['md5']
  location, size = do_upload(id, meta, body)

  logger.debug "Updating image #{id} meta:"
  logger.debug "Setting status to 'available'"
  logger.debug "Setting location to '#{location}'"
  logger.debug "Setting size to '#{size}'"
  logger.debug "Setting checksum to '#{checksum}'"
  vms.put_image(id, status: 'available', uploaded_at: Time.now, location: location, size: size, checksum: checksum)
end