在计算巢多租户版全托管服务模式中,如果服务商需要自己管理服务实例(即资源)的生命周期,则需要服务商在自己的服务中实现SPI接口。用户在计算巢中进行创建、续费和删除服务实例的操作时,计算巢会携带服务实例参数调用SPI接口。本文介绍了服务商应该遵循的服务回调地址SPI接口规范。
前提条件
SPI接口支持如下三种操作。
Action | 说明 |
---|---|
CreateServiceInstance | 创建服务实例 |
DeleteServiceInstance | 删除服务实例 |
RenewServiceInstance | 续费服务实例 |
创建服务实例CreateServiceInstance
该接口是用于计算巢回调服务地址创建服务实例。接口规范如下:
- 每次创建的服务实例会生成一个服务实例唯一标识(serviceInstanceId)。
- 请不要阻塞此接口,若耗时较长,可使用队列做缓冲,设置status=creating,然后立即返回。计算巢会轮询直到返回status=created为止。
- 该接口可能会被多次调用,如果被重复调用,请借助服务实例唯一标识(serviceInstanceId)保证接口的幂等性。
该接口的格式要求如下:
参数名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
token | String | 是 | 安全校验令牌 |
action | String | 是 | 创建服务实例createServiceInstance |
aliUid | String | 是 | 用户ID |
serviceInstanceId | String | 是 | 服务实例ID |
serviceParameters | String | 是 | 输入参数 |
serviceId | String | 是 | 服务ID |
commodityCode | String | 否 | 云市场商品ID |
specificationCode | String | 否 | 云市场套餐ID |
components | String | 否 | 云市场额外计费项 |
endTime | String | 否 | 服务实例到期时间 |
以回调地址是http://www.isvwebsite.com为例,计算巢携带输入参数的调用请求示例如下:
http://www.isvwebsite.com?
token=097a56e26a93c874140bbcdf403f3f60&action=createServiceInstance&aliUid=123123323&serviceId=svc-xxx&serviceInstanceId=xxx&serviceParameters=\"{\\\\"vpcId\\\\":\\\\"xxx\\\\"}\\"
返回示例如下:
{
"status": "created",
"outputs": {
"frontEndUrl": "http://yourdomain.com/",
"adminUrl": "http://yourdomain.com/admin",
"username": "admin",
"password": "admin_password"
}
}
删除服务实例DeleteServiceInstance
该接口是用于计算巢回调服务地址删除服务实例。接口规范如下:
- 删除服务实例实现的效果,由服务商自行定义,例如删除该服务实例下的云资源或者其他。
- 请不要阻塞此接口,若耗时较长,可使用队列做缓冲,设置status=deleting,然后立即返回。计算巢会轮询直到返回status=deleted为止。
- 该接口可能会被多次调用,如果被重复调用,请借助服务实例唯一标识(serviceInstanceId)保证接口的幂等性。
该接口的格式要求如下:
参数名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
token | String | 是 | 安全校验令牌 |
action | String | 是 | 删除服务实例deleteServiceInstance |
aliUid | String | 是 | 用户ID |
serviceInstanceId | String | 是 | 服务实例ID |
serviceId | String | 是 | 服务ID |
以服务回调地址是http://www.isvwebsite.com为例,计算巢携带输入参数的调用请求示例如下:
http://www.isvwebsite.com?
token=097a56e26a93c874140bbcdf403f3f60&action=deleteServiceInstance&aliUid=123123323&serviceId=svc-xxx&serviceInstanceId=xxx
返回示例如下:
{
"status": "deleted"
}
续费服务实例RenewServiceInstance
该接口是用于计算巢回调服务地址续费服务实例。接口规范如下:
- 请不要阻塞此接口,若耗时较长,可以使用队列做缓冲,设置status=renewing,然后立即返回。计算巢会轮询直到返回status=renewed为止。
- 该接口可能会被多次调用,如果被重复调用,请借助服务实例唯一标识(serviceInstanceId)保证接口的幂等性。
该接口的格式要求如下:
参数名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
token | String | 是 | 安全校验令牌 |
action | String | 是 | 续费服务实例RenewServiceInstance |
aliUid | String | 是 | 用户ID |
serviceInstanceId | String | 是 | 服务实例ID |
serviceParameters | String | 是 | 输入参数 |
serviceId | String | 是 | 服务ID |
endTime | String | 否 | 服务实例到期时间 |
以服务回调地址是http://www.isvwebsite.com为例,计算巢携带输入参数的调用请求示例如下:
http://www.isvwebsite.com?
token=097a56e26a93c874140bbcdf403f3f60&action=renewServiceInstance&aliUid=123123323&serviceId=svc-xxx&serviceInstanceId=xxx
返回示例如下:
{
"status": "renewed"
}
安全检验令牌生成规则
计算巢使用HmacSha256消息验证码生成算法和服务密钥(K)计算API请求输入参数(m)的Hmac值(C),并以C的十六进制编码做为安全校验令牌(Token)。
编码方式:Token = Hex( HMACSha256( K, m ) )
- Hex:十六进制编码函数。
- HMACSha256:消息验证码生成函数。
- K:服务密钥,可于控制台服务详情页查看获取。
- m:API请求输入参数,对参数名称按照字典排序后,通过=和&进行拼接。
安全检验令牌生成示例如下:
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Hex;
public class TokenGenerationExample {
private final String SERVICE_KEY = "1038bb06d5964d5cb5eb";
private final Map<String, String> PARAMETERS = new HashMap<>();
{
PARAMETERS.put("action", "createServiceInstance");
PARAMETERS.put("serviceId", "service-a");
PARAMETERS.put("serviceInstanceId", "si-x");
PARAMETERS.put("serviceParameters", "{\"InstanceType\":\"mysql.small\", \"ZoneId\":\"cn-shanghai-g\", " +
"\"DataDiskCategory\":\"cloud_efficiency\", \"DataDiskSize\": \"40\", " +
"\"DBRootPassword\":\"passw0RD\"}");
PARAMETERS.put("aliUid", "123456");
}
public String run() throws NoSuchAlgorithmException, InvalidKeyException {
String m = buildFromParameters(PARAMETERS);
//m is action=createServiceInstance&aliUid=123456&serviceId=service-a&serviceInstanceId=si-x&serviceParameters={"InstanceType":"mysql.small", "ZoneId":"cn-shanghai-g", "DataDiskCategory":"cloud_efficiency", "DataDiskSize": "40", "DBRootPassword":"passw0RD"}
byte[] k = getKey(SERVICE_KEY);
byte[] c = generateToken(k, m.getBytes(StandardCharsets.UTF_8));
String token = Hex.toHexString(c);
//token is 3022dbf5ecb5ec75afbd430974878bc0655a0a4e50a32b2f6995169d699d8acd
return token;
}
private String buildFromParameters(Map<String, String> parameters) {
List<Map.Entry<String, String>> entries = new ArrayList<>(parameters.entrySet());
entries.sort((Entry.comparingByKey()));
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : entries) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
return sb.substring(0, sb.length() -1);
}
private byte[] getKey(String keyString) {
return Hex.decode(keyString);
}
public byte[] generateToken(byte[] k, byte[] m) throws NoSuchAlgorithmException, InvalidKeyException {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKey key = new SecretKeySpec(k, "HmacSHA256");
hmac.init(key);
hmac.update(m);
return hmac.doFinal();
}
}