文档

计算巢SaaS SPI接口规范

更新时间:

在计算巢多租户版全托管服务模式中,如果服务商需要自己管理服务实例(即资源)的生命周期,则需要服务商在自己的服务中实现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();
    }
}