Sunday, February 28, 2021

Let's Go Redis!!

This step was ridiculously easy. Amazon linux extras has a topic for Redis 4. It installs with a single command. The server defaults are exactly right for what's installed out of the box. Once installed, all I need to do is start the service.

Am I setup as a cluster? No.

Am I saving the data to the disk or is it only present in memory? I'm not sure.

Do I have the correct number of databases? No clue.

None of that matters. 

For once I just wanted something to work and it did.

Setting Up The Nginx Server

This part was, simply put, a hot mess.

It turns out that I don't really understand how configuration files in Nginx work. Sure I know how they're generally structured. Yeah I know that there are directives. Of course I know that there are locations. That being said, when I was configuring an Nginx server in the past I guess all I was really doing was just copying functional configuration files from one server to another.

I have literally 400 lines in my command history on my http server that are just repetition of something like six different sets of characters.


Even now when the server is properly working I can't really describe what it's doing. Requests are coming in and ending up in the right place? It seems like there's more to it than that. 

I'm happy to report though that the two new path configuration values are being properly added to the request that's passed along to php-fpm.

I definitely want to spend more time in the future messing around with Nginx configuration files so that I end up in a state where I'm actually writing configurations to achieve a specific end, rather than just munging directives until I get something that works. That's going to have to wait though because I so very much want to start working on the client application.


Saturday, February 27, 2021

Deploying Mabel To Production

I thought this would be the easiest step in the process outlined in the prior post. I just needed to pull the updated code from github onto the server, run composer to install all the necessary dependencies and then create the .ini file that held the environment specific configurations.

When I attempted to run composer I found out I was missing two php extensions (ext-mbstring and ext-xml). I installed each of them in turn and was eventually able to get all the way through a composer install. It's a little odd that I needed to install ext-xml as it's supposed to be installed by default. In amazon-linux-extras that's apparently not the case.


I did NOT run into the problem I had earlier where composer was choking while attempting to unzip files. As expected, this was a vagrant/virtualbox only problem. I'll share some additional details on this (now solved) problem in a future post.

Last but not least, I needed to update the ini file that contained environment specific configuration values. One of those values is the path to the service specific log file. I already knew where I wanted log files to go, but while going through this I realized I'd made a mistake during development.

The error handler for the service contains a path to a special log file for unhandled errors and exceptions. The problem here is that this path was under source control. If I set the path to the correct value on my local machine it'd be incorrect for the production server. If I set it to the correct value for the production server it'd be incorrect on my local. This value couldn't be loaded from the service configuration file because the state of the service with respect to code executing in this file is unknown. In other words, one of the cases that can route execution into this file is a malformed configuration file. 

I did some thinking and some googling around this problem and stumbled upon what become my preferred solution. Henceforth, the two external paths that this service (or any other service) depends on (the path to the project and the path to the log files) will be provided externally by the configuration files of Nginx itself.


Friday, February 26, 2021

Setting Up The Production Server

Now that the code for Mabel is in a functional enough state that I'd be willing to publicly expose it, the next logical step before doing that is to finish setting up the production infrastructure. 

This is gonna entail a couple of steps:

  1. Deploy Mabel and configure it.
  2. Configure the Nginx server to properly server requests for the api.
  3. Install Redis and configure the connection to it.
  4. Configure the RDS database that Mabel will be communicating with.

Once these are all done I can actually start working on the CLIENT application for Robot Fortress.

Mabel Needs Unit Tests

A while back, I wrote a post about updating a big chunk of existing unit tests in the php-common library so that they worked with php 7.4 and phpunit 7. I knew that there was a much more recent version of phpunit, but I didn't want to tackle familiarizing myself with the up-to-date version by converting an existing set of unit tests.

So, given that I needed to write unit tests for the code I'd added to Mabel I figured it was the perfect time to dive into PHPUnit 9.

I have to say that I was pleasantly surprised. The api for PHPUnit is a lot cleaner now. It's not significantly different but it's more streamlined. The organizational structure is more obvious with a clear hierarchy that helps you to select the right methods for the case you're dealing with. The documentation is less complicated and more straightforward as a result and I found that to be a great help.

Case in point, I was writing a test that required me to mock an object representing an instantiation of the Predis/Client class. That particular class uses a lot of "PHP magic" that allows php code to interact with the a live instance of redis. Unfortunately, because of that magic, the class didn't actually contain the methods I needed to mock so that they would return the correct values for my tests.

As it turns out, the old-school way of handling this (use the setMethods method of the mock builder) was deprecated, and while my unit tests all passed the results were besmirched with deprecation warnings. Digging into the code I figured out that, by design, there's no way to suppress deprecation warnings in PHPUnit. Basically, the editorialization here is that you should upgrade your code to the current api rather than figure out how force your tests into a passing state.

Looking at the specific method that had been deprecated I saw that it had a link to a pull request in the github for phpunit. Amazingly, this pull request had an entire comment thread associated with it that:

  1. Explained a corner case that a user was concerned about (which happened to be similar to my situation)
  2. A suggestion of methods to add to the api to address the users concern.
  3. A discussion of the suggestion that resulted in the decision to implement largely what the user suggested.
  4. A description of the schedule by which these changes would be made to the api.
Honestly, the whole thing just made me feel very confident about phpunit as a piece of software and php as a whole. It obviously not a dying language and both the language and the community around it are obviously improving and evolving in a direction of more simplicity, clarity, logic and performance.

Anyhow, the unit tests for the current revision of Mabel are done.

Thursday, February 25, 2021

Status: 401 Computer Says No

Another day of work and now I think I can safely make this teeny tiny API public.

Basically, mabel expects that all requests sent to it will have a special header field. The value of this header field will be pulled from the http request object and checked against a persistent store of known header fields. 

If the request doesn't have the special header field, the value of the special field isn't valid, the value isn't found in the persistent store or the value is found but access should not be granted, the api will return a 401 HTTP response.

Unless of course the api endpoint is intended to be public. Then mabel doesn't care whether you're authenticated or not.

The most difficult part of adding this bit of functionality was boiling the tangle of ideas I started with this morning into a discrete set of cases that I could implement in the code in a way that didn't make me squint my eyes with suspicion as I wrote it.

Just like I wish I had a Dev-Ops expert to farm out the details of that work, I also wish I had someone I could pass this to for code review. I think the code I wrote is okay, but I do feel a little weird about it. Like there's probably a better or more concise way to do what I set out to accomplish. 

Oh well.

Tomorrow I'm going to write unit tests using whatever the most up-to-date version of php-unit is.

Then I'm going to put this up on my little nano server in AWS and switch over to FINALLY attempting to build something in Unity.

Redis Is Super Simple

Having written an authorization service once before, I know that I'm going to need something fast and lightweight to store information that will allow the server to determine whether or not a client has been properly authenticated every it attempts to send a request.

In the past, I used Redis to do this (and a lot of other things) and I don't see any good reason not to keep using it for development purposes.

I'll take another look at AWS Elasticache at some point, but for now, I don't want this to be a blocker to progress.

So yeah, installing Redis on my local machine was super easy as version 4 (most current version is 6) is available via amazon linux extras and I can install predis to make it easy to interact with the service via php.

I'm going to use the default configuration that comes with the installation for now, but I know that if I were to install this in production on it's own machine I'd want to configure it differently.

It Did Not Take Me Two Months To Update To Slim 4

It only took me a day.

What was I doing during the previous two months? Nothing related to the project, unfortunately.

Anyhow, upgrading to Slim 4 was pretty straightforward. The biggest roadblock was that slim no longer comes bundled with a dependency injection container.

Normally I'd just swap in pimple as that's what I've used since forever. Unfortunately, pimple is apparently not PSR-11 compliant and Slim 4 requires any container used to be PSR-11 compliant. Their documentation referenced (which I interpreted as a reccomendation) something call PHP-DI, so I decided to just use that.

PHP-DI is pretty straightforward to use, though it did take me a while to figure out how best to get the dependencies into the container and how best to get them back out when I needed them.

As with pimple, the PHP-DI container is just an object that stores key/value pairs. The container object doesn't implement array access so you can't treat it like an array when convenient, which is something I was used to with pimple. Theoretically, you can directly set KVP's to the container object, but the documentation discourages that. Rather, you should use a container builder class to set your dependencies because this will eventually allow you to "compile" the container in production and gain performance improvements as a result. Okay.

Unfortunately, this meant that the old way I used to load dependencies into the container wouldn't work. When I was using pimple I 'registered' dependencies to the container by calling a series of 'provider' classes. The only real benefit to these classes were that they allowed me to store some code in a series of organized and well labeled files. So, not really that important and maybe bordering on gold-plating.

With the new container, I decided to go with brutally straightforward. The dependencies are all loaded into the container in the index file that contains all the bootstrapping and the routing for the service. I don't "like" the way the dependencies are handled, but only because the current form offends my artistic sensibilities.

So yeah, now I can really build Mabel.