Introduction to Resources API for Providers¶
Providers deal almost exclusively with UCloud through resource provider APIs.
Rationale¶
We have already told you about the end-user APIs for resources. UCloud uses resources to synchronize work between UCloud/Core and the provider. We achieve this synchronization through two different APIs:
The ingoing API (Provider): This API handles requests, ultimately, from the end-user. UCloud/Core proxies the information from the end-user. During the proxy-step, UCloud/Core performs validation, authentication, authorization and auditing.
The outgoing API (Control): The outgoing API is the provider’s chance to send requests back to UCloud/Core. For example, we use this API for: auditing, updates and queries about the catalog.
In this document, we will cover the ingoing API. This API, in most cases, mirrors the end-user API for write operations. UCloud expands the API by replacing most request types with a fully-qualified form. This means we replace specifications and references with full resource objects.
A Note on the Examples¶
The examples in this section follow the same scenario as the end-user API.
Table of Contents¶
1. Examples
Description |
---|
Creation of Resources |
Looking up resources by provider generated ID |
Dealing with failures |
Dealing with partial failures |
2. Remote Procedure Calls
Name | Description |
---|---|
retrieveProducts |
Retrieve product support for this provider |
create |
Request creation of resource. |
delete |
Request deletion of resource. |
init |
Request from the user to (potentially) initialize any resources |
updateAcl |
Callback received by the Provider when permissions are updated |
verify |
Invoked by UCloud/Core to trigger verification of a single batch |
Example: Creation of Resources¶
Frequency of use | Common |
---|---|
Actors |
|
Communication Flow: Kotlin
/* In this example, we show a simple creation request. The creation request is always initiated by a
user. */
ResourceProvider.create.call(
bulkRequestOf(ExampleResource(
createdAt = 1635170395571,
id = "1234",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "1234",
)),
ucloud
).orThrow()
/*
BulkResponse(
responses = listOf(null),
)
*/
/* In this case, the provider decided not to attach a provider generated ID. */
/* The provider can, at a later point in time, retrieve this resource from UCloud/Core. */
ResourceControl.retrieve.call(
ResourceRetrieveRequest(
flags = ExampleResourceFlags(
filterCreatedAfter = null,
filterCreatedBefore = null,
filterCreatedBy = null,
filterIds = null,
filterProductCategory = null,
filterProductId = null,
filterProvider = null,
filterProviderIds = null,
filterState = null,
hideProductCategory = null,
hideProductId = null,
hideProvider = null,
includeOthers = false,
includeProduct = false,
includeSupport = false,
includeUpdates = false,
),
id = "1234",
),
provider
).orThrow()
/*
ExampleResource(
createdAt = 1635170395571,
id = "1234",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "1234",
)
*/
Communication Flow: Curl
# ------------------------------------------------------------------------------------------------------
# $host is the UCloud instance to contact. Example: 'http://localhost:8080' or 'https://cloud.sdu.dk'
# $accessToken is a valid access-token issued by UCloud
# ------------------------------------------------------------------------------------------------------
# In this example, we show a simple creation request. The creation request is always initiated by a
# user.
# Authenticated as ucloud
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/ucloud/PROVIDERID/example" -d '{
"items": [
{
"id": "1234",
"specification": {
"start": 0,
"target": 100,
"product": {
"id": "example-compute",
"category": "example-compute",
"provider": "example"
}
},
"createdAt": 1635170395571,
"status": {
"state": "RUNNING",
"value": 10,
"resolvedSupport": null,
"resolvedProduct": null
},
"updates": [
{
"timestamp": 1635170395571,
"status": "We are about to start counting!",
"newState": "PENDING",
"currentValue": null
},
{
"timestamp": 1635170395571,
"status": "We are now counting!",
"newState": "RUNNING",
"currentValue": 10
}
],
"owner": {
"createdBy": "user",
"project": null
},
"permissions": {
"myself": [
"ADMIN"
],
"others": [
]
}
}
]
}'
# {
# "responses": [
# null
# ]
# }
# In this case, the provider decided not to attach a provider generated ID.
# The provider can, at a later point in time, retrieve this resource from UCloud/Core.
# Authenticated as provider
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/control/retrieve?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&id=1234"
# {
# "id": "1234",
# "specification": {
# "start": 0,
# "target": 100,
# "product": {
# "id": "example-compute",
# "category": "example-compute",
# "provider": "example"
# }
# },
# "createdAt": 1635170395571,
# "status": {
# "state": "RUNNING",
# "value": 10,
# "resolvedSupport": null,
# "resolvedProduct": null
# },
# "updates": [
# {
# "timestamp": 1635170395571,
# "status": "We are about to start counting!",
# "newState": "PENDING",
# "currentValue": null
# },
# {
# "timestamp": 1635170395571,
# "status": "We are now counting!",
# "newState": "RUNNING",
# "currentValue": 10
# }
# ],
# "owner": {
# "createdBy": "user",
# "project": null
# },
# "permissions": {
# "myself": [
# "ADMIN"
# ],
# "others": [
# ]
# }
# }
Communication Flow: Visual
Example: Looking up resources by provider generated ID¶
Frequency of use | Common |
---|---|
Actors |
|
Communication Flow: Kotlin
ResourceProvider.create.call(
bulkRequestOf(ExampleResource(
createdAt = 1635170395571,
id = "1234",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "1234",
)),
ucloud
).orThrow()
/*
BulkResponse(
responses = listOf(FindByStringId(
id = "mhxas1",
)),
)
*/
ResourceControl.browse.call(
ResourceBrowseRequest(
consistency = null,
flags = ExampleResourceFlags(
filterCreatedAfter = null,
filterCreatedBefore = null,
filterCreatedBy = null,
filterIds = null,
filterProductCategory = null,
filterProductId = null,
filterProvider = null,
filterProviderIds = "mhxas1",
filterState = null,
hideProductCategory = null,
hideProductId = null,
hideProvider = null,
includeOthers = false,
includeProduct = false,
includeSupport = false,
includeUpdates = false,
),
itemsPerPage = null,
itemsToSkip = null,
next = null,
sortBy = null,
sortDirection = null,
),
provider
).orThrow()
/*
PageV2(
items = listOf(ExampleResource(
createdAt = 1635170395571,
id = "1234",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "1234",
)),
itemsPerPage = 100,
next = null,
)
*/
Communication Flow: Curl
# ------------------------------------------------------------------------------------------------------
# $host is the UCloud instance to contact. Example: 'http://localhost:8080' or 'https://cloud.sdu.dk'
# $accessToken is a valid access-token issued by UCloud
# ------------------------------------------------------------------------------------------------------
# Authenticated as ucloud
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/ucloud/PROVIDERID/example" -d '{
"items": [
{
"id": "1234",
"specification": {
"start": 0,
"target": 100,
"product": {
"id": "example-compute",
"category": "example-compute",
"provider": "example"
}
},
"createdAt": 1635170395571,
"status": {
"state": "RUNNING",
"value": 10,
"resolvedSupport": null,
"resolvedProduct": null
},
"updates": [
{
"timestamp": 1635170395571,
"status": "We are about to start counting!",
"newState": "PENDING",
"currentValue": null
},
{
"timestamp": 1635170395571,
"status": "We are now counting!",
"newState": "RUNNING",
"currentValue": 10
}
],
"owner": {
"createdBy": "user",
"project": null
},
"permissions": {
"myself": [
"ADMIN"
],
"others": [
]
}
}
]
}'
# {
# "responses": [
# {
# "id": "mhxas1"
# }
# ]
# }
# Authenticated as provider
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/control/browse?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&filterProviderIds=mhxas1"
# {
# "itemsPerPage": 100,
# "items": [
# {
# "id": "1234",
# "specification": {
# "start": 0,
# "target": 100,
# "product": {
# "id": "example-compute",
# "category": "example-compute",
# "provider": "example"
# }
# },
# "createdAt": 1635170395571,
# "status": {
# "state": "RUNNING",
# "value": 10,
# "resolvedSupport": null,
# "resolvedProduct": null
# },
# "updates": [
# {
# "timestamp": 1635170395571,
# "status": "We are about to start counting!",
# "newState": "PENDING",
# "currentValue": null
# },
# {
# "timestamp": 1635170395571,
# "status": "We are now counting!",
# "newState": "RUNNING",
# "currentValue": 10
# }
# ],
# "owner": {
# "createdBy": "user",
# "project": null
# },
# "permissions": {
# "myself": [
# "ADMIN"
# ],
# "others": [
# ]
# }
# }
# ],
# "next": null
# }
Communication Flow: Visual
Example: Dealing with failures¶
Frequency of use | Common |
---|---|
Actors |
|
Communication Flow: Kotlin
ResourceProvider.create.call(
bulkRequestOf(ExampleResource(
createdAt = 1635170395571,
id = "1234",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "1234",
)),
ucloud
).orThrow()
/*
500 Internal Server Error
*/
Communication Flow: Curl
# ------------------------------------------------------------------------------------------------------
# $host is the UCloud instance to contact. Example: 'http://localhost:8080' or 'https://cloud.sdu.dk'
# $accessToken is a valid access-token issued by UCloud
# ------------------------------------------------------------------------------------------------------
# Authenticated as ucloud
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/ucloud/PROVIDERID/example" -d '{
"items": [
{
"id": "1234",
"specification": {
"start": 0,
"target": 100,
"product": {
"id": "example-compute",
"category": "example-compute",
"provider": "example"
}
},
"createdAt": 1635170395571,
"status": {
"state": "RUNNING",
"value": 10,
"resolvedSupport": null,
"resolvedProduct": null
},
"updates": [
{
"timestamp": 1635170395571,
"status": "We are about to start counting!",
"newState": "PENDING",
"currentValue": null
},
{
"timestamp": 1635170395571,
"status": "We are now counting!",
"newState": "RUNNING",
"currentValue": 10
}
],
"owner": {
"createdBy": "user",
"project": null
},
"permissions": {
"myself": [
"ADMIN"
],
"others": [
]
}
}
]
}'
# 500 Internal Server Error
Communication Flow: Visual
Example: Dealing with partial failures¶
Frequency of use | Common |
---|---|
Actors |
|
Communication Flow: Kotlin
/* In this example, we will discover how a provider should deal with a partial failure. */
ResourceProvider.create.call(
bulkRequestOf(ExampleResource(
createdAt = 1635170395571,
id = "1234",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "1234",
), ExampleResource(
createdAt = 1635170395571,
id = "51214",
owner = ResourceOwner(
createdBy = "user",
project = null,
),
permissions = ResourcePermissions(
myself = listOf(Permission.ADMIN),
others = emptyList(),
),
specification = ExampleResource.Spec(
product = ProductReference(
category = "example-compute",
id = "example-compute",
provider = "example",
),
start = 0,
target = 100,
),
status = ExampleResource.Status(
resolvedProduct = null,
resolvedSupport = null,
state = State.RUNNING,
value = 10,
),
updates = listOf(ExampleResource.Update(
currentValue = null,
newState = State.PENDING,
status = "We are about to start counting!",
timestamp = 1635170395571,
), ExampleResource.Update(
currentValue = 10,
newState = State.RUNNING,
status = "We are now counting!",
timestamp = 1635170395571,
)),
providerGeneratedId = "51214",
)),
ucloud
).orThrow()
/*
500 Internal Server Error
*/
/* In this case, imagine that the provider failed to create the second resource. This should
immediately trigger cleanup on the provider, if the first resource was already created. The provider
should then respond with an appropriate error message. Providers should not attempt to only
partially create the resources. */
Communication Flow: Curl
# ------------------------------------------------------------------------------------------------------
# $host is the UCloud instance to contact. Example: 'http://localhost:8080' or 'https://cloud.sdu.dk'
# $accessToken is a valid access-token issued by UCloud
# ------------------------------------------------------------------------------------------------------
# In this example, we will discover how a provider should deal with a partial failure.
# Authenticated as ucloud
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/ucloud/PROVIDERID/example" -d '{
"items": [
{
"id": "1234",
"specification": {
"start": 0,
"target": 100,
"product": {
"id": "example-compute",
"category": "example-compute",
"provider": "example"
}
},
"createdAt": 1635170395571,
"status": {
"state": "RUNNING",
"value": 10,
"resolvedSupport": null,
"resolvedProduct": null
},
"updates": [
{
"timestamp": 1635170395571,
"status": "We are about to start counting!",
"newState": "PENDING",
"currentValue": null
},
{
"timestamp": 1635170395571,
"status": "We are now counting!",
"newState": "RUNNING",
"currentValue": 10
}
],
"owner": {
"createdBy": "user",
"project": null
},
"permissions": {
"myself": [
"ADMIN"
],
"others": [
]
}
},
{
"id": "51214",
"specification": {
"start": 0,
"target": 100,
"product": {
"id": "example-compute",
"category": "example-compute",
"provider": "example"
}
},
"createdAt": 1635170395571,
"status": {
"state": "RUNNING",
"value": 10,
"resolvedSupport": null,
"resolvedProduct": null
},
"updates": [
{
"timestamp": 1635170395571,
"status": "We are about to start counting!",
"newState": "PENDING",
"currentValue": null
},
{
"timestamp": 1635170395571,
"status": "We are now counting!",
"newState": "RUNNING",
"currentValue": 10
}
],
"owner": {
"createdBy": "user",
"project": null
},
"permissions": {
"myself": [
"ADMIN"
],
"others": [
]
}
}
]
}'
# 500 Internal Server Error
# In this case, imagine that the provider failed to create the second resource. This should
# immediately trigger cleanup on the provider, if the first resource was already created. The provider
# should then respond with an appropriate error message. Providers should not attempt to only
# partially create the resources.
Communication Flow: Visual
Remote Procedure Calls¶
retrieveProducts
¶
Retrieve product support for this provider
Request | Response | Error |
---|---|---|
Unit |
BulkResponse<ExampleResourceSupport> |
CommonErrorMessage |
This endpoint responds with the Product
s supported by
this provider along with details for how Product
is
supported. The Product
s must be registered with
UCloud/Core already.
create
¶
Request creation of resource.
Request | Response | Error |
---|---|---|
BulkRequest<ExampleResource> |
BulkResponse<FindByStringId> |
CommonErrorMessage |
delete
¶
Request deletion of resource.
Request | Response | Error |
---|---|---|
BulkRequest<ExampleResource> |
BulkResponse<Unit> |
CommonErrorMessage |
init
¶
Request from the user to (potentially) initialize any resources
Request | Response | Error |
---|---|---|
ResourceInitializationRequest |
Unit |
CommonErrorMessage |
This request is sent by the client, if the client believes that initialization of resources might be needed. NOTE: This request might be sent even if initialization has already taken place. UCloud/Core does not check if initialization has already taken place, it simply validates the request.
updateAcl
¶
Callback received by the Provider when permissions are updated
Request | Response | Error |
---|---|---|
BulkRequest<UpdatedAclWithResource<ExampleResource>> |
BulkResponse<Unit> |
CommonErrorMessage |
This endpoint is mandatory for Providers to implement. If the Provider does not need to keep
internal state, then they may simply ignore this request by responding with 200 OK
. The
Provider MUST reply with an OK status. UCloud/Core will fail the request if the Provider does
not acknowledge the request.
verify
¶
Invoked by UCloud/Core to trigger verification of a single batch
Request | Response | Error |
---|---|---|
BulkRequest<ExampleResource> |
Unit |
CommonErrorMessage |
This endpoint is periodically invoked by UCloud/Core for resources which are deemed active. The Provider should immediately determine if these are still valid and recognized by the Provider. If any of the resources are not valid, then the Provider should notify UCloud/Core by issuing an update for each affected resource.