title: Google bucket file uploading not working in production environment
body: <p>My platform has an issue uploading to google cloud storage that I'm only experiencing in the production environment and that is real head-scratcher. My platform uses NodeJS and this file upload is being done on the backend in an API service. </p>
  
  <p>Language: Javascript, <code>nodejs version 6.5</code></p>
  
  <p>Packages used: <code>@google-cloud/storage version 0.2.0</code>, <code>gm version 1.23.0</code></p>
  
  <p>The service essentially takes the image in base 64, creates a buffer of the image, then pipes that image to the google cloud bucket. Easy peasy.</p>
  
  <p>Unfortunately its not working on production, and I cannot figure out why. The exact same code runs in production, it just points to a different bucket.</p>
  
  <p><strong>Some relevant code at the top of the file</strong></p>
  
  <pre><code>var gm = require('gm').subClass({ imageMagick: true });
  var Promise = require('bluebird');
  var Q = require('q');
  var request = require('request').defaults({ encoding: null });
  var gcs = require('@google-cloud/storage')({
        keyFilename: sails.config.gcloud.keyFileName,
        projectId: sails.config.gcloud.projectId
  });
  var bucket = gcs.bucket(sails.config.gcloud.buckets.cdn);
  
  // Takes raw base64Image from client and converts it into:
  // {
  //      meta: data:image/jpeg;base64,
  //      data: hugeStringOfImageDataInBase64,
  //      dateType: image/jpeg
  // }
  function decodeImage(base64Image) {
      // Not using regex due to perf.
      var startImageData = base64Image.indexOf(',');
      var regex = /^data:(.+);base64$/;
      var meta = base64Image.slice(0, startImageData);
      var data = base64Image.slice(startImageData + 1);
      var dataType = meta.match(regex)[1];
  
      return { type: dataType, meta: meta, data: data };
  }
  </code></pre>
  
  <p><strong>Relevant code where buffer is being created</strong></p>
  
  <pre><code>createNewPictureAndUploadToCDNBase: function(imageInBase64, imageBuffer, cb) {
  
      return Picture.create()
          .then(function(emptyPicture) {
              var buffer;
  
              if (imageInBase64) {
                  buffer = PictureService.bufferFromBase64(imageInBase64);
              } else {
                  buffer = imageBuffer;
              }
  
              return PictureService.resizeAndUploadPictureToCDN(emptyPicture.id, buffer, function (err, results) {
  
                  emptyPicture = _.merge(emptyPicture, results);
  
                  return emptyPicture.save(function(err) {
                      cb(err, emptyPicture);
                  });
              });
  
          }).catch(function(err) {
              return cb(err);
          });
  },
  
  bufferFromBase64: function (imageInBase64) {
      return new Buffer(decodeImage(imageInBase64).data, 'base64');
  },
  </code></pre>
  
  <p><strong>Main part of the code where file's being piped to Google Cloud Storage bucket</strong></p>
  
  <pre><code>resizeAndUploadPictureToCDN: function (pictureId, imageBuffer, cb) {
  
      var resizingOptions = {
          original: {
              quality: 80,
              size: {}
          },
          thumbnail: {
              quality: 75,
              size: {
                  width: 60,
                  height: 60
              }
          },
          medium: {
              quality: 65,
              size: {height: 250}
          }
      };
  
      var resizingPromises = _.mapValues(resizingOptions, function (options, resizingName) {
  
          return new Promise(function (resolve, reject) {
              var file = bucket.file(pictureId + '-' + resizingName);
  
              // NOTE: 'gm' is a call to GraphicMagick for node
              // More can be found here: https://github.com/aheckmann/gm
  
              gm(imageBuffer)
                  .quality(options.quality)
                  .resize(options.size.width || null, options.size.height || null)
                  .stream()
                  .pipe(file.createWriteStream())
                  .on('finish', function(err, success) {
  
                      file.getMetadata(function(err, metadata) {
                          if (err) {
                              console.log("Error getting metadata from google cloud", err);
                              return reject(err);
                          }
  
                          resolve(metadata.mediaLink);
                      });
  
                  }).on('error', function(err) {
                      console.log("Got an error uploading to Cloud Storage:", err);
                      reject(err);
                  });
          });
      });
  
      Promise.props(resizingPromises)
          .then(function (results) {
              cb(null, results);
          }).error(cb);
  },
  </code></pre>
  
  <p>Don't worry much about that resizing language, I'm confident that part's not the issue. Just takes the images and uploads a few different versions with different sizes. I'm much more confident that will work the same locally as on production than the filestream and interacting with Google Cloud Storage. </p>
  
  <p>So yeah. When I run that locally, uploading to one bucket, it works great. When its run in production, using a different bucket, it fails to upload the image. </p>
  
  <p>However, in both cases I do not get an error message. Instead when it fails, I still get that the file was uploaded successfully. Just when I visit the location of that file, on production it's completely empty of any data. </p>
  
  <p><strong>Read out from production environment of that console.log under <code>file.getMetaData()</code></strong></p>
  
  <pre><code>Corresponding metadata:  
  {
      kind: 'storage#object',
      id: 'cdn.texasca.com/5a3d938b84d560010012b0b3-thumbnail/1513984907989412',
      selfLink: 'https://www.googleapis.com/storage/v1/b/cdn.texasca.com/o/5a3d938b84d560010012b0b3-thumbnail',
      name: '5a3d938b84d560010012b0b3-thumbnail',
      bucket: 'cdn.texasca.com',
      generation: '1513984907989412',
      metageneration: '1',
      timeCreated: '2017-12-22T23:21:47.915Z',
      updated: '2017-12-22T23:21:47.915Z',
      storageClass: 'STANDARD',
      timeStorageClassUpdated: '2017-12-22T23:21:47.915Z',
      size: '0',
      md5Hash: '1B2M2Y8AsgTpgAmY7PhCfg==',
      mediaLink: 'https://www.googleapis.com/download/storage/v1/b/cdn.texasca.com/o/5a3d938b84d560010012b0b3-thumbnail?generation=1513984907989412&amp;alt=media',
      crc32c: 'AAAAAA==',
      etag: 'CKSji6XhntgCEAE=' 
  }
  </code></pre>
  
  <p>Notice how it says <code>size '0'</code>  as in the file size is 0. Everything else looks <strong>exactly</strong> the same as it does after streaming a file to the other google bucket from the local environment, except for it has a file size like '13908' or some such and when you visit the <code>mediaLink</code> location it has a full-size image, not just nothing.</p>
  
  <p>I'm really having trouble debugging...</p>
  
  <p>Where should I start with debugging such an issue? Are there commonly used ways of debugging a filestream? Does anyone know why I'm not getting an error message from this filestream? </p>
  
  <p>Really trying to avoid a full rewrite of this part of my platform.</p>
  
  <p>Any and all help is greatly appreciated!</p>
  
  <p><strong>THINGS I'VE TRIED SINCE INITIALLY POSTING QUESTION</strong></p>
  
  <p>So to fix this issue, I've tried a few things so far. The first one was creating new keys for the service account. So I did that, linked that key in my code and tried it. Same issue, works in local development mode, doesn't work in production (get that weird non-error where it doesn't throw an error but the object size is <strong>0</strong>).</p>
  
  <p>Also, as I mentioned there's two buckets. One is named <code>dev0.cdn.texasca.com</code> and the other is named <code>cdn.texasca.com</code>. In local development mode I switched it to uploading images to <code>cdn.texasca.com</code> instead of the dev one and... BOOM! Still same issue. Works great when I run the platform locally but fails in production. </p>
  
  <p>I also tried giving my service account more "roles" thinking that might solve the issue. Has made no difference.</p>
  
  <p>That's all I've tried so far. Feeling really blocked on this issue so I'm moving on to some other things for a day or two so I can get some productive features and bug fixes out. Hoping for some more suggestions!</p>