Control Plane in 5 minutes
Nile is a platform that makes it easy to build a reliable, multi-tenant control plane.
This quickstart will walk you through Nile's REST APIs and the first steps of using Nile. In the next 5 minutes, you'll create an entity to represent your data plane and then use Nile's APIs to sign up tenant and manage data plane instances as that tenant.
Prerequisites
Before getting started, you will need access to Nile Platform. This includes URL of Nile backend and Nile Developer login details. You'll also want to invent a name for your first Nile workspace.
You can follow the tutorial using either curl
or ts-node
with Nile's Javascript SDK. We start by setting up the environment:
- curl
- Typescript
export NILE_URL=https://prod.thenile.dev:443
export NILE_WORKSPACE_ID=clustify
export NILE_DEVELOPER_EMAIL=nora@clustify.com
export NILE_DEVELOPER_PASSWORD=verysecret
First, install Nile's JS SDK:
npm install @theniledev/js
# or
yarn add @theniledev/js
Once you installed Nile's JS SDK, you need to initialize the client:
import Nile, { Entity, Organization} from "@theniledev/js";
const NILE_URL = "https://prod.thenile.dev:443"
const NILE_WORKSPACE = "clustify"
const NILE_DEVELOPER_EMAIL = "nora@clustify.com"
const NILE_DEVELOPER_PASSWORD = "verysecret1!"
const nile = Nile({
basePath: NILE_URL,
nile.workspace = NILE_WORKSPACE
});
Login to Nile as a developer
First, lets get a developer identification token that will allow us to create entities in Nile:
- curl
- Typescript
curl -X POST "$NILE_URL/auth/login" \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "'$NILE_DEVELOPER_EMAIL'",
"password": "'$NILE_DEVELOPER_PASSWORD'"
}'
the result will be something like:
{"token" : "eyJhbGciOiJIUzI1NiJ9...9Q64"}
Save the token to an environment variable, since we'll be using it a lot:
export NILE_DEVELOPER_TOKEN=eyJhbGciOiJIUzI1NiJ9...9Q64
// Log in to Nile as a developer
await nile.developers.loginDeveloper({
loginInfo: {
email: NILE_DEVELOPER_EMAIL,
password: NILE_DEVELOPER_PASSWORD,
},
});
nile.authToken = nile.developers.authToken;
console.log("Successfully logged into nile!")
Create your workspace
Workspace is how Nile represents your control plane. This is where entities and tenants are created. You already picked a name for it, so now we just need to ask Nile to create it:
- curl
- Typescript
curl -X POST "$NILE_URL/workspaces" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $NILE_DEVELOPER_TOKEN" \
--data-raw '{
"name": "'$NILE_WORKSPACE_ID'"
}'
await nile.workspaces.createWorkspace({
createOrganizationRequest: { name: NILE_WORKSPACE},
}).then( (ws) => { if (ws != null) console.log("Successfully created Workspace: " + ws.name)})
Create your first custom entity
It is typical to start by creating an entity that represents your data plane system. In this case, we'll describe a MySQL cluster, running on AWS.
- curl
- Typescript
- Create a file named
clusters.json
and populate it with the entity schema:
{
"name": "clusters",
"schema": {
"type": "object",
"properties": {
"cluster_name": { "type": "string" },
"status": { "type": "string" },
"ARN": { "type": "string" },
"Endpoint": { "type": "string" }
},
"required": ["cluster_name"]
}
}
- Now update Nile with the new entity:
curl -X POST "$NILE_URL/workspaces/$NILE_WORKSPACE_ID/entities" \
--header "Authorization: Bearer $NILE_DEVELOPER_TOKEN" \
--header 'Content-Type: application/json' \
-d @clusters.json
const entityDef: Entity = {
"name": "clusters",
"schema": {
"type": "object",
"properties": {
"cluster_name": { "type": "string" },
"status": { "type": "string" },
"ARN": { "type": "string" },
"Endpoint": { "type": "string" }
},
"required": ["cluster_name"]
}
};
await nile.entities.createEntity({
"entity": entityDef
}).then((data) =>
{
console.log('API called successfully. Returned data: ' + JSON.stringify(data, null, 2));
}).catch((error:any) =>
console.error(error.message));
This did two important things:
- Added an entity names
clusters
to your workspace. Users who sign up to your control plane will be able to manage clusters. - Auto-generated OpenAPI spec and REST APIs for CRUD operations on the cluster entity.
Sign up as a tenant
Lets make up an imaginary user of our control plane. We'll call him Shaun.
- curl
- Typescript
export NILE_TENANT_EMAIL=shaun@colton.demo
export NILE_TENANT_PASSWORD=mycatname
Sign up as the user:
curl -X POST "$NILE_URL/workspaces/$NILE_WORKSPACE_ID/users" \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "'$NILE_TENANT_EMAIL'",
"password": "'$NILE_TENANT_PASSWORD'"
}'
const NILE_TENANT_EMAIL="shaun@colton.demo"
const NILE_TENANT_PASSWORD="mycatname"
await nile.users.createUser({
"createUserRequest" : {
"email" : NILE_TENANT_EMAIL,
"password" : NILE_TENANT_PASSWORD
}
}).then ( (usr) => {
if (usr != null)
console.log("User " + usr.email + " was created")
})
And lets log in as Shaun. The rest of the quickstart will be done from Shaun's POV as a user of Clustify:
- curl
- Typescript
curl -X POST "$NILE_URL/workspaces/$NILE_WORKSPACE_ID/auth/login" \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "'$NILE_TENANT_EMAIL'",
"password": "'$NILE_TENANT_PASSWORD'"
}'
If successful, you should get a token as a result. Once again, we'll save it for reuse:
export NILE_TENANT_TOKEN=eyJhbGciOiJIUzI1NiJ9...9Q64
await nile.users.loginUser({
"loginInfo": {
"email": NILE_TENANT_EMAIL,
"password": NILE_TENANT_PASSWORD
}
})
nile.authToken = nile.users.authToken
In Nile, everything happens in tenant organizations, so lets create a tenant for Shaun and his colleagues.
- curl
- Typescript
curl -X POST "$NILE_URL/workspaces/$NILE_WORKSPACE_ID/orgs" \
--header "Authorization: Bearer $NILE_TENANT_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Colton Labs"
}'
If all went well, you will see something like this:
{"id":3,"type":"organization","name":"Colton Labs"}
Save the organization id because the user can only manage clusters in organizations they are a member of.
export NILE_TENANT_ORG=3
const NILE_TENANT_NAME = "Colton Labs"
await nile.organizations.createOrganization({"createOrganizationRequest" :
{
"name" : NILE_TENANT_NAME,
}}).then ( (org) => {
if (org != null) {
console.log("Tenant " + org.name + " was created")
tenant_id = org.id // Save the organization id since most actions happen in organization context
}
}).catch((error:any) => console.error(error.message));
Manage clusters
Time for Shaun to manage some clusters.
But wait, you may be thinking, I don't have a control plane yet! How can Shaun manage clusters? Truth is, you actually kinda already have a control plane. At least parts of it.
Lets have Shaun create his first cluster:
- curl
- Typescript
curl -X POST "$NILE_URL/workspaces/$NILE_WORKSPACE_ID/orgs/$NILE_TENANT_ORG/instances/clusters" \
--header "Authorization: Bearer $NILE_TENANT_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"cluster_name": "MyFirstCluster"
}'
await nile.entities.createInstance({
"org" : tenant_id,
"type" : entityDef.name,
"body" : {
"cluster_name" : "MyFirstCluster"
}
}).then((cluster) => console.log ("cluster was created: " + JSON.stringify(cluster, null, 2)))
If all went well, the response will be something like this:
{
"id": 35,
"type": "clusters",
"properties": {
"cluster_name": "MyFirstCluster"
}
}
and if you now try to list the clusters:
- curl
- Typescript
curl -X GET "$NILE_URL/workspaces/$NILE_WORKSPACE_ID/orgs/$NILE_TENANT_ORG/instances/clusters" \
--header "Authorization: Bearer $NILE_TENANT_TOKEN"
nile.entities.listInstances({
"org": tenant_id,
"type": entityDef.name
}).then((clusters) => {
console.log("You have the following clusters:")
console.log(clusters)
})
You should see:
[
{
"id": 35,
"type": "clusters",
"properties": {
"cluster_name": "MyFirstCluster"
}
}
]
Try to create a few more! Perhaps with more than just the required field. You can also update the cluster with PUT
or delete it with DELETE
.
Congratulations!
You took the first few steps toward using Nile to build a SaaS control plane.
What's next?
- You can continue exploring our REST API or our JS SDK.
- You can learn to connect a data plane to this control plan though our pulumi and kubernetes examples
- You can also explore examples for additional use-cases
- Learn more about Nile: Entities, users, organizations, custom entities and events.