Print this page
OS-2573 metadata agent support for PUT, DELETE and V2 protocol

*** 1,8 **** --- 1,10 ---- + // vim: set ts=4 sts=4 sw=4 et: var VM = require('/usr/vm/node_modules/VM'); var ZWatch = require('./zwatch'); var common = require('./common'); + var crc32 = require('./crc32'); var async = require('/usr/node/node_modules/async'); var execFile = require('child_process').execFile; var fs = require('fs'); var net = require('net'); var path = require('path');
*** 481,490 **** --- 483,494 ---- var cmd; var parts; var val; var vmobj; var want; + var reqid; + var req_is_v2 = false; parts = rtrim(data.toString()).replace(/\n$/, '') .match(/^([^\s]+)\s?(.*)/); if (!parts) {
*** 493,509 **** } cmd = parts[1]; want = parts[2]; ! if (cmd === 'GET' && !want) { write('invalid command\n'); return; } vmobj = self.zones[zone]; if (cmd === 'GET') { zlog.info('Serving ' + want); if (want.slice(0, 4) === 'sdc:') { want = want.slice(4); --- 497,519 ---- } cmd = parts[1]; want = parts[2]; ! if ((cmd === 'NEGOTIATE' || cmd === 'GET') && !want) { write('invalid command\n'); return; } vmobj = self.zones[zone]; + // Unbox V2 protocol frames: + if (cmd === 'V2') { + if (!parse_v2_request(want)) + return; + } + if (cmd === 'GET') { zlog.info('Serving ' + want); if (want.slice(0, 4) === 'sdc:') { want = want.slice(4);
*** 616,625 **** --- 626,696 ---- + which_mdata)); return; } }); } + } else if (cmd === 'NEGOTIATE') { + if (want === 'V2') { + write('V2_OK\n'); + } else { + write('FAILURE\n'); + } + return; + } else if (req_is_v2 && cmd === 'DELETE') { + want = want.trim(); + + if (!want) { + returnit(new Error('Invalid DELETE Request')); + return; + } + + setMetadata(want, null, function (err) { + if (err) { + returnit(err); + } else { + returnit(null, 'OK'); + } + }); + } else if (req_is_v2 && cmd === 'PUT') { + var key; + var value; + var terms; + + terms = want.trim().split(' '); + + if (terms.length !== 2) { + returnit(new Error('Invalid PUT Request')); + return; + } + + // PUT requests have two space-separated BASE64-encoded + // arguments: the Key and then the Value. + key = (new Buffer(terms[0], 'base64')).toString().trim(); + value = (new Buffer(terms[1], 'base64')).toString(); + + if (!key) { + returnit(new Error('Invalid PUT Request')); + return; + } + + if (key.slice(0, 4) === 'sdc:') { + returnit(new Error('Cannot update the "sdc" Namespace.')); + return; + } + + zlog.info('PUT of key "' + key + '"'); + setMetadata(key, value, function (err) { + if (err) { + zlog.error(err, 'could not set metadata (key "' + key + + '")'); + returnit(err); + } else { + returnit(null, 'OK'); + } + }); + + return; } else if (cmd === 'KEYS') { addMetadata(function (err) { var ckeys = []; var ikeys = [];
*** 730,763 **** --- 801,933 ---- return; }); } + function setMetadata(_key, _value, cb) { + var payload = {}; + var which = 'customer_metadata'; + + // Some keys come from "internal_metadata": + if (_key.match(/_pw$/) || _key === 'operator-script') { + which = 'internal_metadata'; + } + + // Construct payload for VM.update() + if (_value) { + payload['set_' + which] = {}; + payload['set_' + which][_key] = _value; + } else { + payload['remove_' + which] = [ _key ]; + } + + zlog.trace({ payload: payload }, 'calling VM.update()'); + VM.update(vmobj.uuid, payload, zlog, cb); + } + + function parse_v2_request(inframe) { + var m; + var m2; + var newcrc32; + var framecrc32; + var clen; + + m = inframe.match( + /\s*([0-9]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+(.*)/); + if (!m) { + zlog.error('V2 frame did not parse: ', inframe); + return (false); + } + + clen = Number(m[1]); + if (!(clen > 0) || clen !== (m[3] + ' ' + m[4]).length) { + zlog.error('V2 invalid clen: ' + m[1]); + return (false); + } + + newcrc32 = crc32.crc32_calc(m[3] + ' ' + m[4]); + framecrc32 = m[2]; + if (framecrc32 !== newcrc32) { + zlog.error('V2 crc mismatch (ours ' + newcrc32 + + ' theirs ' + framecrc32 + '): ' + want); + return (false); + } + + reqid = m[3]; + + m2 = m[4].match(/\s*(\S+)\s*(.*)/); + if (!m2) { + zlog.error('V2 invalid body: ' + m[4]); + return (false); + } + + cmd = m2[1]; + want = (new Buffer(m2[2], 'base64')).toString('utf8'); + + req_is_v2 = true; + + return (true); + } + + + function format_v2_response(code, body) { + var resp; + var fullresp; + + resp = reqid + ' ' + code; + if (body) + resp += ' ' + (new Buffer(body).toString('base64')); + + fullresp = 'V2 ' + resp.length + ' ' + crc32.crc32_calc( + resp) + ' ' + resp + '\n'; + + zlog.trace({ response: fullresp }, 'formatted V2 response'); + + return (fullresp); + } + function returnit(error, retval) { var towrite; if (error) { zlog.error(error.message); + if (req_is_v2) + write(format_v2_response('FAILURE', error.message)); + else write('FAILURE\n'); return; } // String value if (common.isString(retval)) { + if (req_is_v2) { + write(format_v2_response('SUCCESS', retval)); + } else { towrite = retval.replace(/^\./mg, '..'); write('SUCCESS\n' + towrite + '\n.\n'); + } return; } else if (!isNaN(retval)) { + if (req_is_v2) { + write(format_v2_response('SUCCESS', retval.toString())); + } else { towrite = retval.toString().replace(/^\./mg, '..'); write('SUCCESS\n' + towrite + '\n.\n'); + } return; } else if (retval) { // Non-string value + if (req_is_v2) + write(format_v2_response('FAILURE')); + else write('FAILURE\n'); return; } else { // Nothing to return + if (req_is_v2) + write(format_v2_response('NOTFOUND')); + else write('NOTFOUND\n'); return; } } };