The recent introduction of affordable ARM64 VPS instances by Hetzner ([introduction](URL_PLACEHOLDER_0)) was a welcome development. I had committed to prioritizing ARM for Objective-S (the native compiler is currently ARM64-only), but affordable VPS providers like Digital Ocean were limited to x86. While a mixed ARM/x86 environment is possible, I wanted to avoid the added complexity. This is why I moved the hosting of [Objective-S site](URL_PLACEHOLDER_1) from Digital Ocean to Oracle Cloud’s free tier, as it was the only cost-effective way to host on ARM. With multiple options available, I felt it was the right time to explore a hunch I’d had.
I’ve believed for a while that there’s a need for something between simple shell scripts (like the excellent [Deployment from Scratch](URL_PLACEHOLDER_2)) and the complexity of Kubernetes ([aircraft carrier](URL_PLACEHOLDER_3)). With the infrastructure in place, I decided to pursue this idea by controlling the Hetzner server API using Objective-S.
Interfacing with the Hetzner API
The Hetzner API documentation ([documentation](URL_PLACEHOLDER_4)) reveals that the base URL is https://api.hetzner.cloud/v1/. We can set up an API scheme handler in Objective-S to communicate with the Hetzner API, including the authentication header and JSON support:
| |
This concise code accomplishes several things: it retrieves the API token from the macOS keychain (keychain:password/hetzner-api/metaobject), inserts it into the Bearer string within a dictionary literal, and defines the api: scheme for interacting with the Hetzner API. For instance, api:servers translates to https://api.hetzner.cloud/v1/servers.
With this setup, we can define a simple class to interact with the API:
| |
This class has two user-facing methods: -images, which lists available images, and -types, which lists server types. The method bodies are brief, leveraging Objective-S’s capabilities. The -schemeNames method enables the api: scheme handler within the class’s methods.
Here’s an example of an interactive st-shell session requesting image types and server types:
| |
The “CAX11” instance type is the entry-level ARM64 instance we’ll be using.
Creating a Server
Creating a VPS involves POSTing a dictionary to the servers endpoint, outlining the desired server properties:
| |
The -create method sends the post: message directly to the endpoint reference.
Interacting with Servers
Once a server is created, we’ll want to interact with it, such as deleting it. While this could be done using methods that require a server_id parameter, it’s cleaner to create a separate server abstraction.
The HetznerHost class is initialized with a server response, extracting the IP address and server ID. The latter defines a server: scheme handler. Its subclassing of MPWRemoteHost will become relevant later.
| |
Similar to the POST request, the DELETE request is handled by sending a delete message to the root reference of the server: scheme.
Retrieving server instances is done via a GET request to the API’s servers endpoint. The collect HOM (Higher Order Messaging) method simplifies mapping from the API’s returned dictionaries to server objects:
| |
You might be thinking that a dedicated class for servers with its scheme handler seems excessive for just sending a DELETE request. Here are some additional capabilities:
| |
With this, we have complete server lifecycle control with concise and straightforward code, thanks to Objective-S’s abstractions like Polymorphic Identifiers, Storage Combinators, and Higher Order Messaging. This control is available both for scripts and reusable objects in other applications.
Installing Objective-S
With the ability to manage virtual servers, let’s explore using them, for instance, to run Objective-S and Objective-S-based web servers. This is where MPWRemoteHost comes into play. It represents a remote host (currently rudimentary) and facilitates SSH connections for executing commands and transferring files via SFTP. The latter is presented as a store, simplifying file creation on remote hosts:
| |
File copying is similarly straightforward:
| |
The script copies a tar archive containing GNUstep and Objective-S libraries, extracting it into the target machine’s '/usr' directory. It also transfers the interactive Objective-S shell (st), the runsite command for serving “.sited” bundles via HTTP, and a .bashrc file for setting up environment variables.
| |
As an extension to MPWHost (the superclass of MPWRemoteHost used as the base for HetznerHost), our server objects can now install Objective-S.
The same applies to server objects for similar scripts managing Digital Ocean droplets.
Conclusion
My initial objective wasn’t to showcase Objective-S but to utilize cloud systems effectively. My hunch was that Objective-S would be well-suited for this task. It exceeded my expectations: its features like Polymorphic Identifiers, first-class references, nested scheme handlers, and Higher Order Messaging seamlessly work together for concise and natural interaction with REST APIs and remote hosts. It bridges the gap between ad-hoc scripting and proper modeling, enabling hackability without creating a mess.