Random Musings on Technology

Pause & Resume VirtualBox VM in OSX on Focus change

 • 

This little script will pause and resume a virtual machine running under VirtualBox when the window focus changes. It resumes (unpause) the machine whenever its window gets the mouse focus, and it pauses the VM whenever it loses focus.

It is useful for when you have an alternate "desktop" within a VM, so that it only runs whenever you are doing something in it.

#!/usr/bin/python

try:  
    from AppKit import NSWorkspace
except ImportError:  
    print "Can't import AppKit -- maybe you're running python from brew?"
    print "Try running with Apple's /usr/bin/python instead."
    exit(1)

from datetime import datetime  
from time import sleep  
import os

last_active_name = None  
while True:  
    active_app = NSWorkspace.sharedWorkspace().activeApplication()
    if active_app['NSApplicationName'] != last_active_name:
        if last_active_name == 'VirtualBox VM':
            os.system('VBoxManage controlvm  db3bd778-3e91-4834-8730-1a6e3f5e59c3 pause')

        last_active_name = active_app['NSApplicationName']

        if last_active_name == 'VirtualBox VM':
            os.system('VBoxManage controlvm  db3bd778-3e91-4834-8730-1a6e3f5e59c3 resume')

        print '%s: %s [%s]' % (
            datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            active_app['NSApplicationName'],
            active_app['NSApplicationPath']
        )
    sleep(1)

Good writeup on migrating from AWS to GCP

 • 

Vidar Hokstad wrote a really good comment on his experience migrating some infrastructure from AWS to GCP in this HN thread. I'm reproducing here verbatim because I think it is pretty insightful:

Unfortunately not, but it's surprisingly straight-forward, apart from the database bit, but here's a bit more detail from memory. There are many ways of doing this and some will depend strongly on which tools you're comfortable with (e.g. nginx vs. haproxy vs. some other reverse proxy is largely down to which one you know best and/or already have in the mix) [Today I might have considered K8s, but this was before that was even a realistic option, but frankly even with K8s I'm not sure -- the setup in question was very simple to maintain]:

  • Set up haproxy, nginx or similar as reverse proxy and carefully decide if you can handle retries on failed queries. If you want true zero-downtime migration there's a challenge here in making sure you have a setup that lets you add and remove backends transparently. There are many ways of doing this of various complexity. I've tended to favour using dynamic dns updates for this; in this specific instance we used Hashicorp's Consul to keep dns updated w/services. I've also used ngx_mruby for instances where I needed more complex backend selection (allows writing Ruby code to execute within nginx)

  • Set up a VPN (or more depending on your networking setup) between the locations so that the reverse proxy can reach backends in both/all locations, and so that the backends can reach databases both places.

  • Replicate the database to the new location.

  • Ensure your app has a mechanism for determining which database to use as the master. Just as for the reverse proxy we used Consul to select. All backends would switch on promoting a replica to master.

  • Ensure you have a fast method to promote a database replica to a master. You don't want to be in a situation of having to fiddle with this. We had fully automated scripts to do the failover.

  • Ensure your app gracefully handles database failure of whatever it thinks the current master is. This is the trickiest bit in some cases, as you either need to make sure updates are idempotent, or you need to make sure updates during the switchover either reliably fail or reliably succeed. In the case I mentioned we were able to safely retry requests, but in many cases it'll be safer to just punt on true zero downtime migration assuming your setup can handle promotion of the new master fast enough (in our case the promotion of the new Postgres master took literally a couple of seconds, during which any failing updates would just translate to some page loads being slow as they retried, but if we hadn't been able to retry it'd have meant a few seconds downtime).

Once you have the new environment running and capable of handling requests (but using the database in the old environment):

  • Reduce DNS record TTL.

  • Ensure the new backends are added to the reverse proxy. You should start seeing requests flow through the new backends and can verify error rates aren't increasing. This should be quick to undo if you see errors.

  • Update DNS to add the new environment reverse proxy. You should start seeing requests hit the new reverse proxy, and some of it should flow through the new backends. Wait to see if any issues.

  • Promote the replica in the new location to master and verify everything still works. Ensure whatever replication you need from the new master works. You should now see all database requests hitting the new master.

  • Drain connections from the old backends (remove them from the pool, but leave them running until they're not handling any requests). You should now have all traffic past the reverse proxy going via the new environment.

  • Update DNS to remove the old environment reverse proxy. Wait for all traffic to stop hitting the old reverse proxy.

  • When you're confident everything is fine, you can disable the old environment and bring DNS TTL back up.

The precise sequencing is very much a question of preference - the point is you're just switching over and testing change by change, and through most of them you can go a step back without too much trouble. I tend to prefer ensuring you do changes that are low effort to reverse first. Need to keep in mind that some changes (like DNS) can take some time to propagate.

EDIT: You'll note most of this is basically to treat both sites as one large environment using a VPN to tie them together and ensure you have proper high availability. Once you do, the rest of the migration is basically just failing over.

NestJS + TypeORM = Doesn't work, very brittle.

 • 

Just needed to write this out. I've been working with some project which was decided to be implemented in NestJS. I have always been wary of using "thick" backend frameworks to implement applications, because I've seen all the problems that arise when a "toy program" becomes a real-life production project using RoR, LoopBack and similar frameworks.

At some point someone convinced me to try NestJS. In theory it is different because it uses several standard libraries and it "just" provides a framework to glue them all.

The problem is that that glue is pretty brittle: As with all frameworks, once you have to deviate a bit from their intended use, you are screwed. Similarly, you will hit a little known bug that is hidden deep inside the framework... and because it is free, nobody will really care if you get blocked (though luck, fix it yourself... if you can/have-time dive into the code). I've got two examples so far:

Using the TypeORM library

We had implemented a good chunk of code in NestJS, and it was working OK. However, at some point we decided to implement the TypeORM library as it is described in NestJS documentation (nothing fancy, just one table using Postgres). While everything works when running the application, the tests are irremediably broken due to this bug:

https://github.com/nestjs/typeorm/issues/321

TypeError: Right-hand side of 'instanceof' is not an object when use absolute path in nestjs

Apparently NestJS + TypeORM do not play well with the Jest framework. The Github issue specifically talks about "using absolute paths", but this happened to us even when not using absolute paths. Of course getting "support" (which is free support because it's free software after all) did not help at all, and in this case, the NestJS team didn't even acknowledge the bug.

Using a RabbitMQ Microservice

The second issue happened when trying to use the "sanctioned" RabbitMQ micro-service configuration. I reported it in the following issue:

https://github.com/nestjs/nest/issues/7196

Basically, a malformed JSON in a RabbitMQ message is not being caught as an exception where it should. The issue has been open for several months now in the "needs triage" state. Good thing I found a workaround!

Workarounds

Because life must go on, I had to find workarounds for these two issues. Fortunately, workarounds were easy to do, but it seemed that I had to "patch" my code for problems happening in the NestJS framework.

What comes next

I would not use NestJS for any future project, nor I would recommend it for anyone. Sure, the prospect of not having to implement a lot of "boilerplate" code sounds good, but in this case, having to dig into some unknown codebase made me lose time that I could have been using for implementing real work.

It will be better to download one of the many NodeJS hexagonal-architecture boilerplates or MVC or NTier or whatever architecture you prefer, and then using standard libraries like Express, Sequelize, Bunny, etc to connect to services. That way if something doesn't work, you will have complete control.

I still prefer thin frameworks.