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’



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

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.



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

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.



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

def insert_meta(meta)
  image     = vms.post_image(meta, address)
  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.



103
104
105
# File 'lib/image/routes/post_image.rb', line 103

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
96
97
# 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(503, 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)
  rescue InternalError => e
    return exit_error(503, 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.



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

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