Skip to main content

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

Deprecation notice

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:

MethodDescription
GETRetrieve some namespaced keys for an NRN
POSTCreates an NRN
PUTFully replaces an NRN content
PATCHModifies specific keys on an NRN
warning

In this API, PUT and PATCH have different effects! PUT is destructive while PATCH is additive.

important

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"
}
}.
}
}
tip

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"
}
Use profiles to separate accounts or regions

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.