loading...

插件实战

华为云 - SSL证书管理

最后一章来个真实例子,实现两个华为云的接口服务

  • 查询证书列表
  • 获取证书详情

接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=SCM&version=V3&api=ListCertificates

在写插件之前请先注册华为云账号,并创建IAM用户,然后了解熟悉这两个接口的功能及使用。如果遇到困难直接给华为云发工单。


编写yml

写插件之前一定先创建一个项目文件夹,再在里面创建plugin.spec.yaml

$ mkdir huaweiyun_scm
cd huaweiyun_scm
touch plugin.spec.yaml
            

yaml遵循插件规范,如有疑问请返回观看插件规范章节。

plugin_spec_version: v2
name: huaweiy_cloud_scm
title:
  en: HUAWEI Cloud - SSL Certificate Manager
  zh-CN: 华为云 - SSL证书管理
description:
  en: "Huawei, together with the world-famous digital certificate service organization, provides you with one-stop certificate lifecycle management to realize the trusted identity authentication and secure data transmission of the website."
  zh-CN: "是华为联合全球知名数字证书服务机构,为您提供一站式证书的全生命周期管理,实现网站的可信身份认证与安全数据传输。"
version: 0.1.0
vendor: chariot
tags:
  - huawei
  - safe
  - api
enable_cache: true
status:
- supported
language: python



types:
  response:
    error:
      title:
        en: error
        zh-CN: "错误"
      type: object
      required: false
    error_code:
      title:
        en: error_code
        zh-CN: "错误编号"
      type: string
      required: false
    error_msg:
      title:
        en: error_msg
        zh-CN: "错误信息"
      type: string
      required: false
    certificates:
      title:
        en: certificates
        zh-CN: "证书列表"
      type: "[]object"
      required: false
    total_count:
      title:
        en: total_count
        zh-CN: "证书数量"
      type: integer
      required: false
    data:
      title:
        en: Data
        zh-CN: "证书详情"
      type: "object"
      required: false


connection:
  domain_name:
    title:
      en: Domain Name
      zh-CN: "IAM用户所属帐号名"
    type: string
    required: true
  name:
    title:
      en: Name
      zh-CN: "IAM用户名"
    type: string
    required: true
  password:
    title:
      en: Password
      zh-CN: "IAM用户的登录密码"
    type: password
    required: true
  project_name:
    title:
      en: Project Name
      zh-CN: "IAM用户所属帐号的项目名称"
    type: string
    required: true
    default: "cn-north-4"
  endpoint:
    title:
      en: Endpoint
      zh-CN: "承载REST服务端点的服务器域名或IP"
    type: string
    required: true
    default: "iam.cn-north-4.myhuaweicloud.com"


actions:
  ListCertificates:
    title:
      en: ListCertificates
      zh-CN: "查询证书列表"
    input:
      endpoint:
        title:
          en: Endpoint
          zh-CN: "承载REST服务端点的服务器域名或IP"
        type: string
        required: true
        default: "antiddos.cn-north-4.myhuaweicloud.com"
      limit:
        title:
          en: limit
          zh-CN: "每页条目数量"
        type: integer
        required: false
      offset:
        title:
          en: offset
          zh-CN: "偏移量"
        type: integer
        desctiption:
          en: "value: [1-30]"
          zh-CN: "取值 - [1-30]"
        required: false
      sort_dir:
        title:
          en: sort_dir
          zh-CN: "排序方式"
        type: string
        required: false
        enum:
          - "ASC"
          - "DESC"
      sort_key:
        title:
          en: sort_key
          zh-CN: "排序依据参数"
        type: string
        required: false
        enum:
          - ""
          - "certExpiredTime"
          - "certStatus"
          - "certUpdateTime"
      status:
        title:
          en: status
          zh-CN: "证书状态"
        type: string
        required: false
        enum:
          - ""
          - "ALL"
          - "PAID"
          - "ISSUED"
          - "CHECKING"
          - "CANCELCHECKING"
          - "UNPASSED"
          - "EXPIRED"
          - "REVOKING"
          - "REVOKED"
          - "UPLOAD"
          - "CHECKING_ORG"
          - "ISSUING"
          - "SUPPLEMENTCHECKING"
    output:
      response:
        title:
          en: Response
          zh-CN: "返回响应"
        type: response
        required: false
  ShowCertificate:
    title:
      en: ShowCertificate
      zh-CN: "获取证书详情"
    input:
      endpoint:
        title:
          en: Endpoint
          zh-CN: "承载REST服务端点的服务器域名或IP"
        type: string
        required: true
        default: "antiddos.cn-north-4.myhuaweicloud.com"
      certificate_id:
        title:
          en: certificate_id
          zh-CN: "证书id"
        type: string
        required: true
    output:
      response:
        title:
          en: Response
          zh-CN: "返回响应"
        type: response
        required: false

生成框架

$ chariot-plugin -g plugin.spec.yaml
$ tree
.
├── actions
│   ├── __init__.py
│   ├── ListCertificates.py
│   ├── models.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── ListCertificates.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   └── ShowCertificate.cpython-39.pyc
│   └── ShowCertificate.py
├── Dockerfile
├── help.md
├── icon.png
├── main.py
├── Makefile
├── plugin.spec.yaml
├── requirements.txt
├── SDK
│   ├── base.py
│   ├── chariot.py
│   ├── cli.py
│   ├── __init__.py
│   ├── models.py
│   ├── plugin.py
│   ├── __pycache__
│   │   ├── base.cpython-39.pyc
│   │   ├── chariot.cpython-39.pyc
│   │   ├── cli.cpython-39.pyc
│   │   ├── __init__.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   ├── plugin.cpython-39.pyc
│   │   ├── subassembly.cpython-39.pyc
│   │   └── web.cpython-39.pyc
│   ├── subassembly.py
│   └── web.py
├── tests
│   ├── ListCertificates.json
│   └── ShowCertificate.json
├── triggers
│   └── models.py
└── util

        

框架生成之后,架构和这大致即可

框架是跟根据yaml生成,所以尽量保持规范。并且还会自动生成数据类型校验文件 models.py

models.py

  • 数据类型校验使用的是pydantictyping,web服务使用的是fastapi框架
  • 如果需要修改SDK可以查看文档https://fastapi.tiangolo.com/features/

编写actions

如果对初始生成的models没有要求的话可以直接编写actions

这时在actions下有3个文件

.
├── __init__.py
├── ListCertificates.py     # yaml对应方法
├── models.py       # 类型校验类文件
└── ShowCertificate.py      #  yaml对应方法
        

首先我们编写ListCertificates.py

from SDK.subassembly import Actions
from SDK.base import Core

from .models import CONNECTION, LISTCERTIFICATESINPUT, LISTCERTIFICATESOUTPUT

import requests
import json

class LISTCERTIFICATESACTIONS(Actions):

    def __init__(self):
        # 初始化

        self.name = "ListCertificates"
        self.inputModel = LISTCERTIFICATESINPUT
        self.outputModel = LISTCERTIFICATESOUTPUT
        self.connModel = CONNECTION


    def connection(self, data={}):
        # write your code

        # 连接器

        domain_name = data.get("domain_name")
        name = data.get("name")
        password = data.get("password")
        project_name = data.get("project_name")
        endpoint = data.get("endpoint")

        try:
            url = f"https://{endpoint}/v3/auth/tokens"

            headers = {
                "Content-Type": "application/json;charset=utf8",
            }

            form_data = {
                "auth": {
                    "identity": {
                        "methods": [
                            "password"
                        ],
                        "password": {
                            "user": {
                                "domain": {
                                    "name": domain_name
                                },
                                "name": name,
                                "password": password
                            }
                        }
                    },
                    "scope": {
                        "project": {
                            "name": project_name
                        }
                    }
                }
            }

            resp = requests.post(url, headers=headers, data=json.dumps(form_data, ensure_ascii=False))

            self.tk = resp.headers.get("X-Subject-Token")

        except Exception as e:
            raise Exception(f"连接器获取IAM token失败 - {str(e)}")

    def run(self, params={}):
        # write your code

        endpoint = params.get("endpoint")
        limit = params.get("limit")
        offset = params.get("offset")
        sort_dir = params.get("sort_dir")
        sort_key = params.get("sort_key")
        status = params.get("status")

        tk = self.tk

        try:

            url = f"https://{endpoint}/v3/scm/certificates"

            headers = {
                "X-Auth-Token": tk,
                "Content-Type": "application/json;charset=utf8",
            }

            ps = {}

            if limit:
                ps.update({"limit": limit})

            if offset:
                ps.update({"offset": offset})

            if sort_dir:
                ps.update({"sort_dir": sort_dir})

            if sort_key:
                ps.update({"sort_key": sort_key})

            if status:
                ps.update({"status": status})

            resp = requests.get(url, headers=headers, params=ps)

            data = resp.json()

            return data

        except Exception as e:
            raise Exception(f"{self.__class__} - {str(e)}")

可以看到,connection()用来创建连接器,其实就是为了获取token,让其加入类属性,然后在run()中使用

当然也可以通过其他手段来获取token,但要记住connection()一定在run()前一步执行


测试打包

插件写完,肯定要测试一下啦,这时就需要真实的账号了。当编写其他插件时,最好有一个可随时测试的环境,这样可以减少很多麻烦。

测试文件在$plugin_name/tests文件下,以json格式传入,在"connection":{}"input":{}里分别填入连接器参数和入参

{
	"version": "v1",
	"type": "action_start",
	"body": {
		"action": "ListCertificates",
		"meta": {},
		"connection": {
			"domain_name": "domain_name",
			"name": "name",
			"password": "password",
			"project_name": "cn-north-4",
			"endpoint": "iam.cn-north-4.myhuaweicloud.com"
		},
		"dispatcher": null,
		"input": {
			"endpoint": "scm.cn-north-4.myhuaweicloud.com"
		}
	}
}
        

执行命令测试chariot-plugin -r tests/ListCertificates.json

{"version": "v1", "type": "action_event", "body": {"output": {"response": {"certificates": [], "total_count": 0}}, "log": "succ", "status": "ok"}}

如果得到的返回和这一样,或者数组不是空数组就对了,插件完成了。这个空数组是因为没有在华为云建立服务.

现在插件编写完成,也测试了,是时候打包上传千乘系统了

$ chariot-plugin -tb
[*] Creating plugin tarball
rm -rf build
rm -rf chariot-huaweiy_cloud_scm-0.1.0.tar.gz
tar -cvzf chariot-huaweiy_cloud_scm-0.1.0.tar.gz --exclude=chariot-huaweiy_cloud_scm-0.1.0.tar.gz --exclude=tests --exclude=run.sh *
Dockerfile
Makefile
SDK/
SDK/plugin.py
...

打包完成后将在项目目录下的得到一个压缩文件chariot-huaweiy_cloud_scm-0.1.0.tar.gz


上传

Example 1 1

结尾

可以看到这个插件有两个action,我们只完成只完成了一个,还有一个和已完成的类似,就当练习吧。

image-missing