All blog posts Behind the scenesKnow-how 5 mins read

Introducing the Nine Self-Service API

Cyrill Troxler
Written by
Cyrill Troxler
Published
May 8, 2023
Share this post

As we launch our API we want to give some technical insights on how we got here and what can be achieved with it.

Over the last two years we have been reworking on how we provision new infrastructure at Nine. The main goals for this were:

  • fully automated to allow customers to self-service nine’s products and services.
  • create a standard approach that can be used in all internal teams

It all started when we got more familiar with writing Kubernetes controllers in Go during our development of Nine Managed GKE. It was around the same time that we discovered Crossplane. It was still early days and it would take quite a bit of time for v1 to be released, so we were being a bit cautious. After some first experiments with trying to manage our Terraform modules with it, we decided to make use of it for our new Object Storage service and built our first controllers using crossplane-runtime.

We have been using this new approach for quite some time now and our frontend Cockpit has also been using the new API since we launched the updated Object Storage in 2021. As we added more services which make use of this new system like our Managed Kubernetes NKE we gained confidence in the system, the APIs and controllers that power it. Having confidence in the system allowed us to take the next step and open it to customers, so they could also consume the API directly.

The main landing page for the new API can be found within our docs on docs.nineapis.ch. In addition to that, the API definitions (Go types and OpenAPI v2 Spec) can be found on GitHub.

CLI

With the launch we also introduced a new CLI tool that helps with authenticating against the API. It’s called nctl and the full source and installation instructions can be found on GitHub.

We implemented a login flow similar to gcloud, simply running nctl auth login <cockpit account name> will redirect you to your browser to enter your Cockpit login credentials. After logging in you can then create a service account to access the API in your own automation workflows.

The CLI implements just a few of our services right now but it also allows you to CRUD any API object from a yaml or json file similar to kubectl with nctl create/apply/delete -f <file>.

Integrations

Besides interacting with the API using nctl there are already other existing integrations that work out of the box. As our API is built on top of Kubernetes anything that can interact with a Kubernetes Custom Resources will be able to speak to our API. We can, for example, leverage Terraform to provision any service on our API or use kubectl to interact with any object on the API once authenticated with nctl.

Terraform

The Kubernetes Terraform provider supports applying any Kubernetes object using the kubernetes_manifest resource. Here’s a minimal example that will create a service account on our API using Terraform. It will also wait until the resource is reported to be ready which can be especially helpful if you later want to retrieve the secret that the service account creates. 

resource "kubernetes_manifest" "sample_cluster" {
manifest = {
apiVersion = "infrastructure.nine.ch/v1alpha1"
kind = "KubernetesCluster"
metadata = {
name = "sample-cluster"
namespace = "<cockpit account name>"
}
spec = {
forProvider = {
vcluster = {
version = "1.24"
}
nodePools = []
location = "nine-es34"
}
writeConnectionSecretToRef = {
name = "sample-cluster"
namespace = "<cockpit account name>"
}
}
}

wait {
condition {
type = "Ready"
status = "True"
}
}
}

# read secret that the KubernetesCluster creates
data "kubernetes_secret_v1" "example_sa" {
metadata {
name = kubernetes_manifest.sample_cluster.manifest.spec.writeConnectionSecretToRef.name
namespace = kubernetes_manifest.sample_cluster.manifest.spec.writeConnectionSecretToRef.namespace
}
}

# put token into an output. This could then be used by another module for example.
output "kubeconfig" {
sensitive = true
value = data.kubernetes_secret_v1.sample_cluster.data.kubeconfig
}

-> Code <-

We can apply this as usual with Terraform and we can see that after the cluster creation is completed, we can read out the secret containing the kubeconfig to access our new cluster.

$ terraform apply
[...]
kubernetes_manifest.sample_cluster: Creating...
kubernetes_manifest.sample_cluster: Still creating... [10s elapsed]
kubernetes_manifest.sample_cluster: Still creating... [20s elapsed]
kubernetes_manifest.sample_cluster: Still creating... [30s elapsed]
kubernetes_manifest.sample_cluster: Still creating... [40s elapsed]
kubernetes_manifest.sample_cluster: Still creating... [50s elapsed]
kubernetes_manifest.sample_cluster: Still creating... [1m0s elapsed]
kubernetes_manifest.sample_cluster: Still creating... [1m10s elapsed]
kubernetes_manifest.sample_cluster: Creation complete after 1m12s
data.kubernetes_secret_v1.sample_cluster: Reading...
data.kubernetes_secret_v1.sample_cluster: Read complete after 0s [id=nine/sample-cluster]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

kubeconfig = <sensitive>

 -> Code <-

For more examples and instructions on how to authenticate against the API with Terraform, head over to GitHub.

Going forward

Our eventual goal is to provide all our services via this API. It will take some time to get there but now that the groundwork has been set, developing new services will be faster than ever and will bring down our need to do manual tasks considerably which in turn allows us to further improve our services.