Featured

Planet Fitness Cancellation Template

Update: I put this in the mail on Tuesday and got cancellation confirmation less than 48 hours later.

I needed to cancel Planet Fitness in the middle of the COVID-19 pandemic and I really didn’t want to be showing up at a gym in person right now (time and unnecessary risk) just to cancel.

I called up my local Planet Fitness and was told I could either come in in person *or* send a [yes, snail mail] letter to the Planet Fitness I signed up at.

So, I’ve thrown together a cheap template to help you include everything you need to cancel (it’s just name, date of birth, and signature, according to the person on the phone.)

You can download the doc I put together at: Planet Fitness Cancellation Word Doc template

Regex testing in test in zsh and launching a browser page

I get so many casual mentions of ticket numbers in our discussions and I keep hacking old URLs for tickets to look them up, or clicking through JIRA boards and views to navigate to the proper ticket, only to have them pull up in a sidebar in JIRA requiring more clicks to get to a useful view.

So, I wrote a quick script in JIRA called gojira to launch a browser window (macOS from the terminal):

open https://jira.example.com/browse/$1

With the above script, I can just invoke gojira PROJECT-1234 to bring up the ticket.

Ok, so that’s great but 90% of the ticket numbers are referencing the same project. So what if I want to only specify a ticket number some times? That’s when I finally looked up if you can test (alias [ ]) a regex in zsh. Turns out that you can.

If regex

The fairly common =~ operator is available for test/[] in zsh. You do have to double the square brackets for it to work (and single quote your regex):

if [[ "$1" =~ '[[:alpha:]]+-[[:digit:]]+' ]]
then
  open https://jira.sdlc.appriss.com/browse/$1
else
  # default to PROJECT if only a number is specified.
  open https://jira.sdlc.appriss.com/browse/PROJECT-$1
fi

Why not a function?

Of course, this can also be handled in a zsh function and added to your .zshrc or .zlogin:

function jira() {
  if [[ "$1" =~ [[:alpha:]] ]]
  then
    open https://jira.sdlc.appriss.com/browse/$1
  else
    open https://jira.sdlc.appriss.com/browse/PROJECT-$1
  fi
}

Deploying a Minecraft Server to EC2 (with some cost analysis)

Caveat… this is likely not the option you want to pursue for a small to medium scale Minecraft server… Lightsail will be more cost-effective due to the amount of data transfer involved. Also, this is partly an exercise it navigating Amazon Linux as a largely Ubuntu user.

Purchase a Domain

I’m going through Namecheap for a .online domain because… well… it’s cheap… and registering revrestfarcenim.online (.online domain through Route 53 would be $39 vs. $1.88 for the first year with Namecheap).

Launch an EC2 instance

If you’re trying to use free-tier resources for this, you’ll want to go for a t2.micro, but you’ll also need to modify the java parameters for the server to fit within those memory limits.

  • Go to Services -> EC2
  • Scroll down in the main window to “Launch instance” and click [Launch Instance]
  • Select Amazon Linux 2 AMI (should be the first option)
  • I’m selecting t3a.small for this to be similar in “virtual” resources as the $10-15/mo virtual server hosting providers (including Amazon Lightsail).
  • Click [Next: Configure Instance Details]
  • You’ll get a default vpc and subnet created and selected… if you don’t want to use these, you can click the available links to create new ones.
  • For “Auto-assign Public IP”, I have “Use subnet setting (Enable)” because I’m going to want to have this publicly accessible.
  • Scroll to near the bottom of the “Step 3” form and find “T2/T3 Unlimited” and uncheck “Enable” unless you want to run up a bill because you forgot about the Minecraft server.
  • Click [Next: Add Storage] to configure your space.
  • Click [Next: Add Tags]
  • Click [Next: Configure Security Group]
  • Click [Add Rule] and add a Custom TCP Rule that allows traffic to port 25565 (the port for Minecraft) from 0.0.0.0/0
  • Click [Review and Launch]
  • Click [Launch] and choose “Create a new key pair” named “minecraftserver”
  • Click on Instances and select your new instance, noting the IPv4 Public IP in the instance details below (leave tab open for reference in the next section)

Create a Hosted Zone in Route 53

  • In a new tab, go to Services -> Route 53 -> Hosted Zones
  • Click [Create hosted zone]
  • Type in your domain name for your server and select “Public hosted zone”
  • Copy the values for the NS record and populate those as the nameservers for your domain (for me, this is on Namecheap
Route 53 NS record
Namecheap Custom DNS settings
  • Now go back to Route 53 and [Create Record]
  • Choose “Simple routing”
  • Click [Define simple record]
  • Leave the record name blank.
  • Under “Value/Route traffic to” select “IP address or another value depending on the record type” and paste your IP in.
  • Be sure “Record type” is “A” and click [Define simple record]
  • Click [Create records] on the “Configure records” screen.

Install and setup Minecraft

  • Using your keypair from instance creation, ssh into your your instance
chmod 400 minecraftserver.pem # or whatever the filename is
ssh -i minecraftserver.pem ec2-user@revrestfarcenim.online
  • Create minecraft server folder and user
sudo mkdir /srv/minecraft-server # assuming EBS mount
sudo adduser --system --home /srv/minecraft-server minecraft
sudo chown minecraft.minecraft /srv/minecraft-service
  • Do updates and install java
sudo yum update
sudo amazon-linux-extras install java-openjdk11
  • Download minecraft server, run for the first time, and set the eula.txt
sudo -u minecraft wget https://launcher.mojang.com/v1/objects/a412fd69db1f81db3f511c1463fd304675244077/server.jar
sudo -u minecraft java -Xmx1024M -Xms1024M -jar server.jar nogui
# you'll get an error, so edit the eula.txt
sudo -u minecraft nano eula.txt # or vi, just set to eula=true
# run minecraft again and try to connect to server at revrestfarcenim.online

Making Minecraft a service

  • Run sudo nano /lib/systemd/minecraft-server.service
  • Paste a config similar to:
[Unit] 
Description=start and stop the minecraft-server 

[Service]
WorkingDirectory=/srv/minecraft-server
User=minecraft
Group=minecraft
Restart=on-failure
RestartSec=20 5
ExecStart=/usr/bin/java -Xms1024M -Xmx1024M -jar server.jar nogui

[Install]
WantedBy=multi-user.target
  • Now, enable the service with sudo systemctl enable /lib/systemd/minecraft-server.service
  • And start the service with sudo systemctl start minecraft-server.service
  • As it’s starting, you can check the status with sudo systemctl status minecraft-server.service.
  • With this systemctl setup, you should also be able to reboot the instance and have the Minecraft server come back up

Troubleshooting

  • If you don’t go with the default gateway and subnets created with the instance, you may find yourself having to explicitly set up an Internet Gateway and a Route Table (0.0.0.0/0 to your igw)
  • Make sure the associated security group is allowing port 25565 to connect (especially if SSH is working)

Teardown

Be sure to delete your hosted zone (you’ll need to delete the A record before deleting the zone) and terminate your instance to avoid running up charges for things you’re not using. I deleted the VPC as well just to avoid clutter and half-baked subnets and security groups, but that’s only because I have nothing of long-term value in the account.

Cost Analysis

There are multiple hits that you’re going to take by hosting this on EC2:

  • egress costs (9¢ per GB after your first GB): In my limited tests, the Minecraft worlds we started up required 100-200MB to initially download per session. It’s unclear if that’s the case for every session, but if you have 20 of your friends use a server, that might be 9¢ x 0.20 GB x 20 for one session each… 36¢ per average number of sessions… that could add up quickly. By contrast, you could get a different hosting provider (including Lightsail) to bundle 2TB of transfer instead.
  • hosted zone cost (50¢ for a distinct domain’s hosted zone)
  • If you use a separate EBS volume, minimum cost there is 80¢.
  • t3a.small cost is 1.88¢ per hour or $13.53/month… you could have the server shut down during off-hours, but then you’re not comparing to “always-on” options.

Monitoring your S3 buckets for #omgcat usage

If you’re serving up a static website from S3, especially if you have larger assets stored there, you may want to put monitoring on the requests or bytes downloaded from S3, just to make sure someone’s not running up terabytes of transfers or millions of requests.

Enabling Metrics on S3

You will have to enable metrics on S3 in order to get CloudWatch alarms on them.

  • Go to Services -> S3 -> Buckets and select the bucket for your static site.
  • Select Management tab and [Metrics] and then click on the pencil icon next to the bucket icon.
  • Once enabled, the metrics will take a bit to populate.

Setting up Simple Notification Service (SNS)

(These are the same instructions as in Monitoring your CloudFront #omgcat usage)

Set up a topic

  • First, you’re going to want to be notified. Go to Services -> Simple Notification Service to set up a pathway for that to happen.
  • Next, click “Topics” and then [Create topic]
  • Name your topic something that adequately describes the purpose (I just used domainname-com)
  • Scroll down to [Create Topic]

Set up a subcription

  • Under “Amazon SNS” left sidebar, click “Subscriptions” and [Create Subscription]
  • Click on the Topic ARN field and you should be able to see an ARN with your topic name as the last part of the ARN. Click that ARN
  • Under Protocol, select your preferred method of notification (I’m going with SMS.
  • Under Endpoint, enter your cell number, including country code (+18005551212 for (800) 555-1212 in the US)

Setting up a CloudWatch Alarm

  • Go to Services -> CloudWatch -> Alarms and [Create alarm]
  • [Select metric] and select S3
  • If you don’t see “Request Metrics per Filter” then the metrics haven’t started populating yet.
  • Check “GetRequests” or “BytesDownloaded” and [Select Metric]
  • Set conditions as you would like to have flag any anomalies and click [Next]
  • Choose “In Alarm” and “Select an existing SNS topic” and click in the box below “Send Notification To…” to get suggestions and select the SNS topic corresponding to the notification method you set up. Click [Next]
  • Name your alarm and click [Next]
  • Review the summary and click [Create Alarm]

Monitoring your CloudFront #omgcat usage

Ok, so you’ve created a lovely static site and/or set up a CloudFront distribution for https for it. But CloudFront bills by the GB. What if somebody decides your assets are perfect to hotlink to or just straight up makes an insane boatload of requests? How do you protect yourself from getting a frightening bill before AWS Budget can even notify you?

Setting up Simple Notification Service (SNS)

Set up a topic

  • First, you’re going to want to be notified. Go to Services -> Simple Notification Service to set up a pathway for that to happen.
  • Next, click “Topics” and then [Create topic]
  • Name your topic something that adequately describes the purpose (I just used domainname-com)
  • Scroll down to [Create Topic]

Set up a subcription

  • Under “Amazon SNS” left sidebar, click “Subscriptions” and [Create Subscription]
  • Click on the Topic ARN field and you should be able to see an ARN with your topic name as the last part of the ARN. Click that ARN
  • Under Protocol, select your preferred method of notification (I’m going with SMS.
  • Under Endpoint, enter your cell number, including country code (+18005551212 for (800) 555-1212 in the US)

Get Notified

  • Go to Services -> CloudFront -> Alarms
  • [Create Alarm]
  • Under Metric, choose the threshold that you want to detect on… maybe it’s Requests, maybe it’s Bytes Downloaded…
  • Select the distribution that you want to watch ( domainname.com should be mentioned in the dropdown)
  • For “Send a notification to”, select the SNS topic that corresponds to the notification method you set up.
  • Since mine is a dev/test site, I don’t expect more than a request/second so
  • Finally, [Create Alarm]

Testing the Alarm

  • If you have a low enough threshold you can probably just hold down F5 (or whatever your refresh key is) for a few seconds. (Word of caution: Don’t do this with a page that downloads a lot of assets!)
  • In bash you can also do the following.
for i in {1..61}
do
  curl https://domainname.com
done
  • If your notifications are working, you should get an message through your preferred notification method.
  • Under Services -> CloudWatch -> Alarms, you should also see your Alarm count be > 0.

Adding a https to an S3 static site via CloudFront

Ok, so we’ve set up a static site hosted from an S3 bucket with a custom domain using Route 53. But sadly, it’s:

Not Secure

Request a Certificate in Certificate Manager

  • Go to Services -> Certificate Manager
  • Click [Request a Certificate]
  • In the window that opens from “Request or Import a Certificate with ACM”, enter your domain name (domainname.com) and click [Next]
  • Select DNS validation and click [Next]
  • Click [Review]
  • Click [Confirm and Request] if the details look correct.
  • Expand the domain in validation:
  • Click [Create record in Route 53] and confirm by clicking [Create] again.
  • You’ll be waiting from several minutes to half an hour for the validation to happen, during which time status will display as “Pending validation”
  • Click [Continue] to finish the request process and go back to the Certificate Manager main screen.
  • Click the (refresh icon) button to update status, and when status turns to “Issued” you are ready to use it in CloudFront.
Pending validation
Ready for use

Setting up a CloudFront distribution

  • In the AWS Console, go to Services → Cloudfront
  • Click [Create Distribution]
  • Click [Get Started] under Web

Create Distribution

  • Under “Origin Domain Name” select the selection under “Amazon S3 buckets” that corresponds to your static web site bucket. (e.g., domainname.com.s3.amazonaws.com)
  • Optional: Restrict Bucket Access [Yes] so that you can control access through the CloudFront distribution alone.
    • Set “Origin Access Identity” to “Create a new identity”
    • Set “Grant Read Permissions on Bucket” to “Yes, Update Bucket Policy”
  • Under Viewer Protocol Policy I select “Redirect HTTP to HTTPS” just to keep things uniform.

Set up SSL

  • Under Alternate Domain Names, enter your domain name (e.g., domainname.com)
  • Select “Custom SSL Certificate”
  • Click “Request or Import a Certificate with ACM”
  • If you go back to CloudFront you should be able to select “Custom SSL Certificate” now and the certificate corresponding to your domain name should show up in suggestions:
  • Scroll down and leave defaults until you get to “Default Root Object”. You’ll want to set this to the name of the document to bring up (e.g., index.html) if the user browses to / on the domain.
  • Optional: I set Logging to On and selected my logging bucket that I used for the static site as the bucket, adding a log prefix for it.
  • To finish, click [Create Distribution]
  • You may be waiting quite a while for changes to propagate to the edge locations, but at some point before the “In Progress” changes to “Deployed” you will be able to pull up via the domain listed under the “Domain Name” column in your list of CloudFront Distributions.

Pointing the domain name at your distribution

  • Go back to Route 53 and go into the hosted zone for your domain name
  • Check the checkbox next to your A record and then go up to Actions -> Edit
  • Change “Value/Route traffic to” from “Alias to S3 endpoint” to “Alias to CloudFront distribution” in the “Choose Distribution” input box.
  • Enter the domain name (“asdfkjdfasoiadsf9u.cloudfront.net.”) as your domain name. (The new interface wasn’t suggesting distributions like the last version of the interface did… it may change next week, of course.)

Locking down S3

If you selected “Restrict bucket access” and had CloudFront update your S3 policy, your public access setting on the bucket is still unaffected. You’ll want to remove that:

  • Go back to Services -> Amazon S3
  • Go to your domainname.com bucket
  • Click Permissions
  • Click Block public access
  • Check “Block all public access” and click [Save]

Some other details

If you want to have JavaScript and forms function properly You’ll want to set up CORS configuration by going to your S3 bucket, then selecting Permissions tab and clicking CORS configuration:

<CORSConfiguration>
 <CORSRule>
   <AllowedOrigin>https://thomaspowell.work</AllowedOrigin>
   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>

   <AllowedHeader>*</AllowedHeader>
 </CORSRule>
 <CORSRule>
   <AllowedOrigin>*</AllowedOrigin>
   <AllowedMethod>GET</AllowedMethod>
 </CORSRule>
</CORSConfiguration>

Some mistakes I made:

  • A certificate for *.domainname.com does not cover domainname.com. You have to add both if you want wildcard and domainname.com itself covered.

Next up… preventing someone from running up a $1,000 AWS bill by hammering your site (i.e., monitoring your site’s access… with better granularity than AWS Budgets…

Hosting a static site on S3 with a purchased domain

S3 Static Site Setup

Creating buckets

  • In your AWS Console, go to Services -> S3
  • Optional:
    • Click [ + Create Bucket ]
    • Type in a bucket name for static site logging (i.e., domainname.com-logs)
    • Accept next all the way to Create Bucket
  • Click [ + Create Bucket ]
  • Type in your domain name (for example, “domainname.com”)
  • If you created a logs bucket:
    • Check “Log requests for access to your bucket” under “Server access logging”
    • Enter the logging bucket name under the “Target bucket” field.
  • Hit [Next] for Configure Options
  • Under Set Permissions, uncheck “Block all public access” and check the box that says “I acknowledge that the current settings may result in this bucket and the objects within becoming public”
  • Click Create Bucket

Creating a static site

  • Drag and drop your static HTML files and assets within the site root in your project and drag to S3. Before sure you have the html file you want to use as an index and as an error page.
  • Click [Next] on the Upload modal.
  • Under “Manage public permissions” change “Do not grant public read access to this object(s)” to “Grant public read access to this object(s)” and click [Next]
  • Click [Upload] from the “Set Properties” step (skip Storage Class configuration, etc. screen).
  • Under the “Properties” tab for your bucket, click on the “Static website hosting” tile
  • Select “Use this bucket to host a website”, enter the name of your index and error documents, and click [Save].
  • You should be able to click the link under endpoint and see your index page.

Create a hosted zone for your domain in Route 53

  • In Route 53, select Hosted zone link on the left of the console
  • Click [Create hosted zone]
  • Enter your Domain name
  • Select “Public hosted zone”
  • Click “Create hosted zone”
  • If you have a domain purchased elsewhere than AWS, copy the name servers under the “Hosted zone details” and set on your domain. (e.g., on Namecheap, it’s under Domain->Nameservers->Custom DNS… BE SURE TO HIT THE GREEN CHECKMARK AFTER EDITS!!)

Create a Record in the Hosted Zone

  • Select “Create record”
  • Select “Simple record”
  • Under “Define simple record”
    • leave the record name blank
    • Value/Route traffic to
      • Alias to S3 website endpoints
      • Select your region
      • In the field that says “Choose S3 Bucket”, you should see your bucket as an option:
  • To finish, click [Define simple record] and then [Create records]

Running Linux on a Mid-2009 MacBook Pro

Apple and their aggressive EOL/EOS policies

I have had an old mid-2009 MacBook Pro (2.26Mhz, Core 2 Duo P7550) that got left behind at macOS 10.11 (El Capitan). I’ve already gone through dealing with end-of-support with a 2011 MBP (no AirPlay server, no longer getting macOS updates, etc…), but the 2009 is old enough that Homebrew itself has dropped support officially. While that doesn’t prevent everything from working, once something won’t install, you either need to fix it yourself or give up.

I was trying to get RDP up and running when I hit that issue, so I figured that it was time to see if I could install an operating system that was still supported on the 2009 MBP.

Ubuntu Linux attempt

I first went down the rabbit hole of Ubuntu, but the first set of instructions I found did not give me a warm fuzzy about being able to install Ubuntu and still have a working machine. I used Rufus to write an Ubuntu ISO to a USB drive and was able to live boot into Ubuntu. Of course, Linux and wifi isn’t always plug and play and the live boot did not recognize the MBP’s wifi card, so it would have been wired or nothing until I installed the OS.

Hello Manjaro

Time for a different strategy. I tried a different strategy and searched for Linux distros for a 2009 MacBook Pro. I decided to go with Manjaro XFCE and was able to write the ISO to my USB drive and have wifi working on live boot, so I swapped MacBook hard drives back to the original 5400 RPM 160GB hard drive that came with it.

Manjaro with XFCE is far snappier than even my 2011 or 2019 MBPs. This is mainly because no bloat and very little running so far, but I’ve also installed Ruby 2.8.0-dev from Manjaro in rbenv on the 2009 (P7550 Core 2 Duo) vs. from the 2019 (i7-9750H 6 core) and the install/build time was 9m31s for the 2009 vs. 11m19s for the 2019 (yes, the 11 year old Mac was faster). This is probably a Clang vs. gcc and readline + openssl installation/build difference between the two environments, but I still found it interesting that the 2009 could win the race even with a head start.

As for system load, the MBP 2009 not actively running anything on macOS 10.11 would register a consistent 2.0+ load and would take several minutes to be usable for typing in iTerm. By contrast, running XFCE, it required several tabs open in Firefox + Terminal + gvim for the load to break 1.0.

Steps to get up and running:

  • Download Manjaro XFCE iso
  • Write image to USB drive using Rufus (dd or whatever if you are adequately skilled at that… Rufus was just painfully easy to kick off from a Windows 10 box)
  • Hold down option while booting to get boot menu.
  • Select your USB drive
  • Boot and verify that Linux is sufficiently usable for your MacBook
  • Install to a hard drive (I recommend using a different hard drive that is booting macOS, but then you’ll need tools for the mounting screws at a minimum.
  • Boot and enjoy

Getting set up

Having used yum, apt, and brew, pamac was a bit of a change. For example, I had to use pamac build ruby-build and pamac build rbenv vs. an install command.

I have starship installed as well, which was relatively straightforward, but I still need to get the fonts right for unicode.

Overall experience

So far I’ve been pleasantly surprised with how useful my 2009 MacBook Pro is again. I’ve managed to type up this blog post without any lag from the processor, and the memory usage is sane as well (despite Firefox). I also miss having the pre-butterfly keyboard keys to type on for writing, so it was nice to experience those again.

Who Else is Participating in Advent of Code 2019

Who else is doing Advent of Code 2019?

I’m using it to try to solve problems in Ruby without actually looking up syntax (looking at you, inject) and getting some practice using MiniTestinstead of RSpec which I use 99% of the time.

Day 2 was kind of fun to be able to implement a tiny virtual machine in Ruby.

Day 3 had me stumped until I just when for a naive solution in which I just created a list of all the coordinates visited instead of trying to figure out an elegant way to calculate the answer… an approach which actually paid off in the second half.

Day 4 has been the problem that I spent the most time fighting with (specifically the second part) after thinking that I had it solved. It was one of those scenarios in which I pretty much starting chasing my own tail after the solution I thought should work didn’t. I ended up with 4 state variables in addition the input variable itself.