Community

Enhancing Ghost.js with Cloudant on IBM codename: BlueMix

Share this post:

As a followup to my last post for deploying Ghost.js on IBM codename:BlueMix, I would like to share a follow-up technique which utilizes a Cloudant CouchDB instance as a persistent image datastore.

TL;DR

Why would I want to do that?

My earlier post generated a healthy amount of comments and inquiry for overcoming the ephemeral nature of file storage that exists in most Platform As a Service technologies, which includes Cloud Foundry and BlueMix. Briefly, BlueMix applications are typically given an ephemeral file system and isolated from read/write access to any persistent disk. The Cloud Foundry community recommends that applications avoid writing to the local file system. [Reference] Some reasons for this include:

  • Local file system storage is short-lived. When apps crash, stop or are restarted, resources can be reclaimed by the platform.
  • Instances of the same app do not share a local file system. Each app runs in its own container. If you scale up instances, both instances will not have access to the same file system.

This creates an interesting quandry. Ghost.js rocks! BlueMix can rapidly deploy, host and scale up a Ghost.js instance, which is awesome! But, BlueMix uses an ephemeral file storage and most bloggers will not tolerate being constrained to using images defined by URI only nor being constantly hassled with having to go upload local image files to some other place. [As an interesting aside: Ghost.js provides a configuration option named filestorage to eliminate the local file upload scenario entirely and be more compatible with PaaS environments]

Enter Cloudant. Let’s exploit BlueMix’s composeable service model to quickly attach a Cloudant CouchDB instance to our deployed Ghost.js app. A couple of BlueMix UI clicks and a quick Cloudant account create should be the elixir to our ills 🙂 I considered MongoDB’s GridFS capability as well, but settled on Cloudant because of its stellar dashboard management and responsive performance as a proven commercial solution. After 5 mins of clicking – well, that also includes one quick email and an instant message, the next chore was to empower Ghost.js to leverage this storage resource. I copied the localfilesystem module and created a new one called cloudant.js. I dutifully tweaked the save and exists functions to interact with Cloudant using the simple, but solid, Nano Node.js package. This resulted in a user experience where I can upload local files into my BlueMix Ghost.js deployment, while having Ghost.js be smart enough to subsequently go and create a representative Cloudant document and attachment which it will then exploit as a linked image resource. Sweet! Ghost.js now always uses URI defined image sources and the user experience remains intact for uploading local files without additional end-user hassle.

If this has piqued your interest, give it a try. You’ll love the results. Here’s how …

  1. Download the latest https://ghost.org/download/
  2. Unzip the download (e.g. unzip Ghost-0.4.2.zip) and change to the base directory (e.g. ghost-0.6.4)
  3. Clone a copy of this git repository and copy the content within the files folder into the root of your ghost installation directory. You will replace two (2) new files (package.json and core/server/storage/index.js), delete one (1) file (npm-shrinkwrap.json), add four (4) new files (config.js, manifest.yml, manifest.cfg and core/server/storage/cloudant.js) and modify one (1) file (core/server/api/upload.js) to the default Ghost.js installation to enable its use.
  4. Open the core/server/api/upload.js and edit line #44
    from:  return Promise.promisify(fs.unlink)(filepath);<br />to: return Promise.promisify(function(){return true});
    Reason: While the local files are temporarily stored on the local filesystem, our new CloudantFileStore actually tells Ghost a url that is an HTTP URL which we’ve generated on-the-fly via a binary attachment into Cloudant. Unlinking is unnecessary and these tmp files will be destroyed during app future app re-staging/restart as a victim of the ephemeral storage paradigm.
  5. Update the name, host and services entries within the manifest.yml to match your deployment environment. The service entries for both the MySQL and Cloudant Service instance names are totally up to you.
  6. Using the BlueMix Web UI, login to BlueMix, and create two (2) back-end database service instances (MySQL and Cloudant) that matches your manifest.yml entries.
    IMPORTANT: When creating the Cloudant Service Instance, the url field should be in the format of <your_cloudant_username>.cloudant.com . Your database can be named anything and your username/password will be whatever was used when setting up your Cloudant account. Be sure to alter your image store database permissions so that reader access is granted to everyone. This is easily done by clicking on the DB within the Cloudant dashboard, then clicking on the Docs dropdown button and choosing permissions. Click the Reader checkbox for the Everybody Else category. A database will automatically be located or created with the format <app_name>-ghost-images for use by Ghost.js during application startup. Permissions are automatically set for Everyone on the database to have read access.
  7. Navigate to the root of the Ghost.js installation directory. Using the cloud foundry v6 cli, login to BlueMix and deploy Ghost.js.
    #Step 0: cf login -a
    <b>$ cf login -a https://api.ng.bluemix.net</b>

    # Step 1: CF_STAGING_TIMEOUT=4 CF_STARTUP_TIMEOUT=4 cf push -s cflinuxfs2
    # CF_STAGING_TIME : Max wait time for app instance startup, in minutes
    # CF_STARTUP_TIMEOUT : Max wait time for app instance startup, in minutes
    # -s : Establishes the stack to use as Trusty Ubuntu 14.04<br /># ENV variables are suggested to accommodate longer staging and startup times.
    <b>$ CF_STAGING_TIMEOUT=4 CF_STARTUP_TIMEOUT=4 cf push -s cflinuxfs2</b>

  8. If all goes well, you should have a running Ghost.js blogging platform. Browse and setup your user. Then visit: <your_app_name>.mybluemix.net/ghost/ .

Testing

There are two (2) ways to embed an image and there are at least two places where images are used within Ghost.js.

  1. Place #1: Settings. You can click on the upload image buttons for a blog cover and blog logo. You will see a little chain-link icon in the bottom left. That toggles the UI from being a file upload to a direct url link to the image. Feel free to give both ways a try, but the Cloudant tweak affects only the local file upload flow. After uploading an image and saving, you can verify if the tweak is working by using the browsers “inspect element” context menu. This should show an image that has a source using a cloudant url pointing to your account. In theory, this should never go away if the account remains in good standing and you never delete the database or document.
  2. Place #2: Content. You can embed an image using the markdown syntax ![alt text](url to image). If you simply type ![an inspiring image of earth] … you will see a familiar image uploader dialog in the preview that can be clicked. Same options hold true, direct web url or local file upload. Same technique to verify if its using Cloudant.

Running Example can be seen @ http://blueghostrox.mybluemix.net/

Cool. Happy Blogging!

More stories
May 7, 2019

We’ve Moved! The IBM Cloud Blog Has a New URL

In an effort better integrate the IBM Cloud Blog with the IBM Cloud web experience, we have migrated the blog to a new URL: www.ibm.com/cloud/blog.

Continue reading

May 1, 2019

Two Tutorials: Plan, Create, and Update Deployment Environments with Terraform

Multiple environments are pretty common in a project when building a solution. They support the different phases of the development cycle and the slight differences between the environments, like capacity, networking, credentials, and log verbosity. These two tutorials will show you how to manage the environments with Terraform.

Continue reading

April 29, 2019

Transforming Customer Experiences with AI Services (Part 1)

This is an experience from a recent customer engagement on transcribing customer conversations using IBM Watson AI services.

Continue reading