Nullplatform Resource Name (NRN)
What is an NRN?
Similar to Amazon ARN's, it's a string that uniquely identifies an entity on nullplatform, giving also contextual information about its parents.
For example, this is an NRN:
organization=1:account=2:namespace=3:application=4:scope=5
The NRN API allows you to set and retrieve information for a specific NRN in a hierarchical way. This means that
if the NRN organization=1:account=2:namespace=3:application=4
has the content { key1: "v1", key2: "v2" }
and
organization=1:account=2:namespace=3:application=4:scope=5
has the content { key2: "x", key3: "v3" }
, the output
for a query on the lower level NRN would result in:
{ key1: "v1", key2: "x", key3: "v3" }
NRN API
This feature might be removed in the future. We recommend using platform settings to manage and configure your organization’s infrastructure and operations. Check our Providers article for more information.
The API is accessible at https://api.nullplatform.com/nrn/:id
.
This is an example response for this API:
GET /nrn/organization=1:account=2:namespace=3:application=4:scope=5?ids=some-key,mynamespace.another-key
{
"nrn": "organization=1:account=2:namespace=3:application=4:scope=5",
"namespaces": {
"global": {
"some-key": "some-value"
},
"mynamespace": {
"another-key": "another-value"
}
},
"profiles": {
... // more on this later
}
}
Some things to consider:
- You must specify the keys to retrieve. This API requires you to specify the keys that will be retrieved using the
ids
query parameter (you can ask for many keys, separated by a comma) - Use the namespace-dot-key convention. The values inside the global namespace can omit the namespace name but in the rest of the cases the namespace name
must be present in this format:
${namespace}.${key}
The available methods are:
Method | Description |
---|---|
GET | Retrieve some namespaced keys for an NRN |
POST | Creates an NRN |
PUT | Fully replaces an NRN content |
PATCH | Modifies specific keys on an NRN |
In this API, PUT and PATCH have different effects! PUT is destructive while PATCH is additive.
Remember that GET
request must specify the keys that will be retrieved through the ids
query string parameter. You can specify
multiple keys at once through a comma-separated list.
NRN values
While NRN values are strings, the API tries to merge object or array values in the hierarchy. To understand this in more detail you can read the following sections.
Object & Array merging
If you store JSON objects or arrays, then the API will try to merge the objects in the hierarchy.
Example:
POST /nrn/organization=1
{
"some_namespace.some_key":"{\"the\":\"thing\"}"
}
POST /nrn/organization=1:account=2
{
"some_namespace.some_key":"{\"another\":\"thing\"}"
}
GET /nrn/organization=1:account=2?ids=some_namespace.some_key
{
"nrn": "organization=1",
"namespaces": {
"some_namespace": {
"some_key":"{\"the\":\"thing\",\"another\":\"thing\"}"
}
},
"profiles": {]
}
Note that the values have been merged, but are still presented as serialized strings.
Output as JSON
If we pass the query string parameter output_json_values=true
, the API will try to output each value as JSON, yielding
these outputs:
POST /nrn/organization=1:account=2
{
"the_number":"1234",
"the_object": "{\"the\":\"thing\",\"another\":\"thing\"}",
"the_list": "[\"hello\"]"
}
GET /nrn/organization=1:account=2?ids=the_number,the_object,the_list&output_json_values=true
{
"nrn": "organization=1",
"namespaces": {
"global": {
"the_number": 1234,
"the_object": {
"the": "thing",
"another": "thing"
},
"the_list": ["hello"]
}
},
"profiles": {}
}
Read an NRN without merging results
If you want to understand what exactly is configured at a specific node in the hierarchy you can send a request using
the no-merge=true
query-string parameter.
For example, if we have this configuration:
this would be the result of querying organization=1:account=2
:
GET /nrn/organization=1:account=2?ids=some_parent_key,some_child_key&no-merge=true
{
"nrn": "organization=1",
"namespaces": {
"global": {
"some_parent_key": null,
"some_child_key": "child"
}
},
"profiles": {}
}
Profiles
NRN has a tree-shaped structure that's handy for hierarchical configurations, but in some cases, you need to place configurations that affect multiple branches of the tree (e.g.: setting the cloud account for all 'development' scopes across all apps). To solve this problem we have created 'profiles' that can be applied to NRN in different branches.
Here's an example diagram for this case:
Profiles hold values in namespaces in the same way as NRN does, but profiles set or override values on NRN.
In the above example, we could have these settings for scope 41:
GET /nrn/organization=1:account=2:namespace=3:application=4:scope=41?ids=some_cloud_provider.serverless_runtime
{
"nrn": "organization=1:account=2:namespace=3:application=4:scope=41",
"namespaces": {
"global": {},
"some_cloud_provider": {
"serverless_runtime": "java_18"
}
},
"profiles": {
// available profiles for this NRN
}
}
But we could have a profile in this organization that defines that testing scopes are to be deployed into a different account:
GET /nrn/organization=1
{
"nrn": "organization=1",
"namespaces": {
"global": {} // there's no NRN configuration for the organization
}, // but some profiles modify the NRN content
"profiles": { // whenever you require the scope configuration with the profile
"testing": {
"some_cloud_provider": {
"account_id": "1234_testing",
"serverless_concurrent_executions": "1"
}
},
"production": {
"some_cloud_provider": {
"account_id": "1234_production",
"serverless_concurrent_executions": "100"
}
}
}
}
Therefore, whenever we ask for the scope configuration under a certain profile, the profile adds/overrides the natural response:
GET /nrn/organization=1:account=2:namespace=3:application=4:scope=41?profile=testing&ids...
{
"nrn": "organization=1:account=2:namespace=3:application=4:scope=41",
"namespaces": {
"global": {},
"some_cloud_provider": {
"serverless_runtime": "java_18"
"account_id": "1234_testing",
"serverless_concurrent_executions": "1"
}
},
"profiles": {
"testing": {
"some_cloud_provider": {
"account_id": "1234_testing",
"serverless_concurrent_executions": "1"
}
},
"production": {
"some_cloud_provider": {
"account_id": "1234_production",
"serverless_concurrent_executions": "100"
}
}.
}
}
The profiles
field always retrieves the available profiles for the NRN that's being requested. The keys are merged with other definitions for the same
profile at higher NRN levels (see the section on hierarchical profiles).
Creating a profile
Profiles can be created just by creating values for it in the same way as regular NRN, but following the naming convention ${profile_name}::${namespace}.${key}
.
Here's an example that generates the profile on the previous example:
POST /nrn/organization=1
{
"testing::some_cloud_provider.account_id": "1234_testing",
"testing::some_cloud_provider.serverless_concurrent_executions": "1",
"production::some_cloud_provider.account_id": "1234_production",
"production::some_cloud_provider.serverless_concurrent_executions": "100"
}
By assigning a specific profile to a scope, you can change the account, region, and virtually any parameter about how / where your application runs.
Checking available profiles
You can check the available profiles for any NRN like this:
GET /nrn/organization=1:account=2/avialable_profiles
{
"nrn": "organization=1:account=2",
"available_profiles": [
"development",
"testing",
"production"
]
}
Reading / Assigning a profile to a scope
The /scope
API has a property that allows assigning a scope with an array of profiles like this:
GET /scope/5
{
"id": 5,
"nrn": "organization=1:account=2:namespace=3:application=4:scope=5"
"profiles": [],
...
}
For example, if we want to mark this scope as production
(given that this profile is already created), we would have
to send this request:
PATCH /scope/5
{
"profiles": ["production"],
}
Composing profiles
Profiles are hierarchical the same way as NRN are hierarchical, meaning that keys inside a profile are composed all the way up until reaching the organization level.
Case: separating teams into different cloud accounts
By applying a specific configuration for your profiles at lower levels (e.g.: namespace) you can make different teams run their applications with different configurations (e.g.: cloud accounts).
Here's an example:
POST /nrn/organization=1
{
"testing::some_cloud_provider.serverless_concurrent_executions": "1",
"production::some_cloud_provider.serverless_concurrent_executions": "100"
}
POST /nrn/organization=1:account=2:namespace=3
{
"testing::some_cloud_provider.account_id": "ns3_testing",
"production::some_cloud_provider.account_id": "ns3_production",
}
POST /nrn/organization=1:account=2:namespace=4
{
"testing::some_cloud_provider.account_id": "ns4_testing",
"production::some_cloud_provider.account_id": "ns4_production",
}
GET /nrn/organization=1:account=2:namespace=3?profile=testing&ids=..
{
"nrn": "oorganization=1:account=2:namespace=3",
"namespaces": {
"global": {},
"some_cloud_provider": {
"account_id": "ns3_testing",
"serverless_concurrent_executions": "1"
}
},
"profiles": {
"testing": {
"some_cloud_provider": {
"account_id": "ns3_testing",
"serverless_concurrent_executions": "1"
}
},
"production": {
"some_cloud_provider": {
"account_id": "ns3_production",
"serverless_concurrent_executions": "100"
}
}
}
}
This configuration is represented in this graph:
Root keys & global namespace
Profiles can hold keys that belong to the profile and are not merged / part of the NRN, these are called root
keys.
Here's an example:
POST /nrn/organization=1
{
"testing::enabled": "false",
}
Note that this convention collides with the convention used to set keys on the global
namespace of an NRN, so to
actually set/override keys on the global
namespace you have to explicitly use the global
keyword when posting
the profile. Here's an example:
POST /nrn/organization=1
{
"testing::enabled": "false",
"testing::global.myKey": "myValue",
}
GET /nrn/organization=1
{
"nrn": "organization=1",
"namespaces": {
"global": {
"myKey": "myValue"
},
},
"profiles": {
"testing": {
"enabled": "false"
}
}
}
Applying multiple profiles
It is possible to apply 2 profiles at the same time by specifying them as a list:
GET /nrn/organization=1:account=2:namespace=3?profile=production,hardened_ami
The only consideration that you need to take is that, in case multiple profiles define the same element, the API will
look at a property called order
inside the profile, giving precedence to lower order
values.
Here's an example:
POST /nrn/organization=1
{
"hardened_ami::order": "1",
"hardened_ami::some_cloud_provider.ami_id": "4321_hardened"
"production::order": "100",
"production::some_cloud_provider.ami_id": "1234_normal",
}
GET /nrn/organization=1:account=2:namespace=3?profile=production,hardened_ami
{
"nrn": "oorganization=1:account=2:namespace=3",
"namespaces": {
"global": {},
"some_cloud_provider": {
"ami_id": "ns3_testing",
"serverless_concurrent_executions": "1"
}
},
"profiles": {
"hardened_ami": {
"order": 1,
"some_cloud_provider": {
"ami_id": "4321_hardened"
}
},
"production": {
"order": 100,
"some_cloud_provider": {
"ami_id": "1234_normal"
}
}
}
}
Removing a namespace or a profile
To clear the namespaces example
and another_example
all the keys inside a namespace you have to execute:
DELETE /nrn/organization=1:account=2?namespaces=example,another_example
Upon successful execution, this will return a 204 No content
. This endpoint requires you to have a role with
nrn:delete
grant (on the NRN being operated).
To clear the development
and testing
profiles execute:
DELETE /nrn/organization=1:account=2?profiles=development,testing
To clear everything execute:
DELETE /nrn/organization=1:account=2
Non-editable keys. If the namespaces/profiles contain keys that you are not entitled to remove, then those keys will be preserved in the namespace and the rest will be deleted.
Removing the namespace on the whole hierarchy. By default, the deletion happens only on the NRN node you're
specifying on the URL. If you want the deletion to happen in the whole hierarchy, you have to pass the
include_parents=true
query-string parameter.
The NRN key is not removed, only the contents. At this time the NRN API removes the contents but not the key itself. Beware that this behavior might change in the future.