Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record the progress of the OSPP 2023 hgctl project #453

Merged
merged 30 commits into from
Oct 27, 2023

Conversation

WeixinX
Copy link
Collaborator

@WeixinX WeixinX commented Jul 27, 2023

1. 简介

该 PR 作为 Draft,用于追踪 OSPP 2023 hgctl 项目进展、审查和修订。
欢迎大家提出修改意见 ~ 👏🏻 ❤️

2. 进度

命令 描述 进度 备注
hgctl plugin build 构建 wasm-go 插件 [√]进一步完善。
[√]tinygo 0.28.1 #462
[√]需要支持 init 得到的 option.yaml
hgctl plugin install/uninstall 安装/卸载 wasm 插件 [√]需要使用 spec.yaml 对配置进行校验。
[√]无需指定插件配置文件,而是直接指定插件代码目录,并完成build(镜像仓库信息可以读取本地配置),install时需要的配置根据schema让用户交互式输入
[√]需要支持 init 得到的 option.yaml
hgctl plugin ls 列出所有已安装的 wasm 插件
hgctl plugin test 在本地进行 wasm 插件测试的相关命令 [√]需要支持 init 得到的 option.yaml
hgctl plugin config wasm 插件配置相关命令
hgctl plugin init wasm 插件项目脚手架

3. hgctl plugin 使用指南

hgctl plugin 相关命令目前可以辅助用户完成 Golang WASM 插件的开发、构建、测试和安装,减轻用户重复复制/粘贴模板的上下文切换负担。

推荐的使用方式(顺序)为:

  1. hgctl plugin init: 初始化 Golang WASM 插件项目;
  2. 用户编写 WASM 插件逻辑;
  3. hgctl plugin build --output-type files: 构建 WASM 插件,在本地输出构建产物;
  4. hgctl plugin test: 使用 docker compose 在本地测试 WASM 插件,如需修改插件逻辑,则返回第 2 步;
  5. hgctl plugin build --output-type image: 构建 WASM 插件作为 OCI 镜像上传至镜像仓库;
  6. hgctl plugin install: 安装 WASM 插件,可以通过本地的 yaml 文件或插件项目进行安装。

另外,若需要查看已安装的插件,则使用 hgctl plugin ls;若需要操作插件配置,则使用 hgctl plugin config 相关命令。

下面分点对上述命令进行说明。

3.1. 使用示例

1. init 初始化插件项目

Kapture 2023-09-07 at 16 58 30

2. build 构建插件

Kapture 2023-09-07 at 17 10 30

3. test 本地测试插件

Kapture 2023-09-07 at 17 28 40

4. install 安装插件

Kapture 2023-09-07 at 17 50 45

5. config 编辑配置

Kapture 2023-09-07 at 18 04 12

3.2. hgctl plugin init

init 命令类似于脚手架工具,可以根据用户的输入生成 Golang WASM 模板项目,该模板项目可以很容易让用户上手插件开发。

3.2.1. 命令选项

$ hgctl plugin init -h
Initialize a Golang WASM plugin project

Usage:
  hgctl plugin init [flags]

Aliases:
  init, i, ini

Examples:
  hgctl plugin init

Flags:
  -h, --help            help for init
  -t, --target string   Directory where the project is initialized (default "./")

3.2.2. 使用示例

$ hgctl plugin init
? Plugin name: hello-world
? Choose a plugin category: custom
? Choose a execution phase: UNSPECIFIED_PHASE
? Execution priority: 10
? Choose a language: zh-CN
? Display name in the plugin market: Hello World
? Description of the plugin functionality: This is a demo plugin
? Display icon in the plugin market:
? Plugin version: 0.1.0
? Name of developer: WeixinX
? Homepage of developer: www.github.com/weixinx
? Email of developer: [email protected]
Initialized the project in "/home/ubuntu/hello-world"

$ tree -a hello-world/
hello-world/
├── .gitignore
├── go.mod
├── main.go
└── option.yaml

0 directories, 4 files

生成的 main.go 文件包含了一个 Hello World 示例,里边形如 @Name 的注解可以用于生成 spec.yaml 作为插件配置校验信息(JSON Schema):

// main.go:

// File generated by hgctl. Modify as required.
// See: https://higress.io/zh-cn/docs/user/wasm-go#2-%E7%BC%96%E5%86%99-maingo-%E6%96%87%E4%BB%B6

package main

import (
	"github.com/tidwall/gjson"
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
	"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
)

func main() {
	wrapper.SetCtx(
		"hello-world",
		wrapper.ParseConfigBy(parseConfig),
		wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
	)
}

// @Name hello-world
// @Category custom
// @Phase UNSPECIFIED_PHASE
// @Priority 10
// @Title zh-CN Hello World
// @Description zh-CN This is a demo plugin
// @IconUrl
// @Version 0.1.0
//
// @Contact.name WeixinX
// @Contact.url www.github.com/weixinx
// @Contact.email [email protected]
//
// @Example
// firstField: hello
// secondField: world
// @End
//
type HelloWorldConfig struct {
	// @Title 第一个字段,注解格式为 @Title [语言] [标题],语言缺省值为 zh-CN
	// @Description 字符串的前半部分,注解格式为 @Description [语言] [描述],语言缺省值为 zh-CN
	firstField string `required:"true"`

	// @Title en-US Second Field, annotation format is @Title [language] [title], language defaults to zh-CN
	// @Description en-US The second half of the string, annotation format is @Description [language] [description], language defaults to zh-CN
	secondField string `required:"true"`
}

func parseConfig(json gjson.Result, config *HelloWorldConfig, log wrapper.Log) error {
	config.firstField = json.Get("firstField").String()
	config.secondField = json.Get("secondField").String()
	return nil
}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config HelloWorldConfig, log wrapper.Log) types.Action {
	err := proxywasm.AddHttpRequestHeader(config.firstField, config.secondField)
	if err != nil {
		log.Critical("failed to set request header")
	}
	return types.ActionContinue
}

生成的 option.yaml 文件非常重要,可以为 build, test, install 命令提供选项,因此不需要用户反复输入很长的命令:

# option.yaml:

# File generated by hgctl. Modify as required.

version: 1.0.0

build:
  # The official builder image version
  builder:
    go: 1.19
    tinygo: 0.28.1
    oras: 1.0.0
  # The WASM plugin project directory
  input: ./
  # The output of the build products
  output:
  # Choose between 'files' and 'image'
    type: files
    # Destination address: when type=files, specify the local directory path, e.g., './out' or
    # type=image, specify the remote docker repository, e.g., 'docker.io/<your_username>/<your_image>'
    dest: ./out
  # The authentication configuration for pushing image to the docker repository
  docker-auth: ~/.docker/config.json
  # The directory for the WASM plugin configuration structure
  model-dir: ./
  # The WASM plugin configuration structure name
  model:
  # Enable debug mode
  debug: false

test:
  # Test environment name, that is a docker compose project name
  name: wasm-test
  # The output path to build products, that is the source of test configuration parameters
  from-path: ./out
  # The test configuration source
  test-path: ./test
  # Docker compose configuration, which is empty, looks for the following files from 'test-path':
  # compose.yaml, compose.yml, docker-compose.yml, docker-compose.yaml
  compose-file:
  # Detached mode: Run containers in the background
  detach: false

install:
  # The namespace of the installation
  namespace: higress-system
  # Use to validate WASM plugin configuration when install by yaml
  spec-yaml: ./out/spec.yaml
  # Installation source. Choose between 'from-yaml' and 'from-go-project'
  from-yaml: ./test/plugin-conf.yaml
  # If 'from-go-project' is non-empty, the output type of the build option must be 'image'.
  from-go-project:
  # Enable debug mode
  debug: false

3.3. hgctl plugin build

build 命令使用容器的方式构建用户编写好的 WASM 插件。

目前支持两种产物输出形式:

  1. files: 将构建产物输出到本地
  2. image: 将构建产物作为 OCI 镜像上传到镜像仓库

3.3.1. 命令选项

$ hgctl plugin build -h
Build Golang WASM plugin

Usage:
  hgctl plugin build [flags]

Aliases:
  build, bld, b

Examples:
  # If the option.yaml file exists in the current path, do the following:
  hgctl plugin build

  # Using "--model(-s)" to specify the WASM plugin configuration structure name, e.g. "BasicAuthConfig"
  hgctl plugin build --model BasicAuthConfig

  # Pushing the build products as an OCI image to the specified repository using "--output-type(-t)" and "--output-dest(-d)"
  docker login
  hgctl plugin build -s BasicAuthConfig -t image -d docker.io/<your_username>/<your_image>


Flags:
  -g, --builder-go string       Golang version in the official builder image (default "1.19")
  -r, --builder-oras string     ORAS version in official the builder image (default "1.0.0")
  -n, --builder-tinygo string   TinyGo version in the official builder image (default "0.28.1")
      --debug                   Enable debug mode
  -a, --docker-auth string      Authentication configuration for pushing image to the docker repository (default "~/.docker/config.json")
  -h, --help                    help for build
  -i, --input string            Directory of the WASM plugin project to be built (default "./")
  -s, --model string            Structure name of the WASM plugin configuration
  -m, --model-dir string        Directory of the WASM plugin configuration structure (default "./")
  -f, --option-file string      Option file for build, test and install (default "./option.yaml")
  -d, --output-dest string      Output destination of the build products (default "./out")
  -t, --output-type string      Output type of the build products. [files, image] (default "files")
  -p, --password string         Password for pushing image to the docker repository
  -u, --username string         Username for pushing image to the docker repository

3.3.2. 使用示例

以 init 命令生成的 Hello World 模板项目为例,介绍目前支持的两种构建输出形式:

1. files: 将构建产物输出至本地(用于测试)

由于 option.yaml 中默认为 files 输出形式,并且指定了输出目录为 ./out 以及配置模型(即 HelloWorldConfig 结构体)所在的目录 ./,因此我们只需通过命令行选项指定模型名称 --model HelloWorldConfig (将该选项写入 option.yaml 也是可以的):

$ cd hello-world
$ hgctl plugin build --model HelloWorldConfig
[-] pull the builder image ...
{"status":"Pulling from plugins/wasm-go-builder","id":"go1.19-tinygo0.28.1-oras1.0.0"}
{"status":"Digest: sha256:9b5a104f4e6bca9fc102a3efc835a2136a8eaa4b76cafbbf93a71098570896c1"}
{"status":"Status: Image is up to date for higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0"}
[√] pull the builder image: higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0
[-] create the builder container ...
[√] create the builder container: ece47f39836cf4da371008c9eb73876cab5228641df1795af99bd50f686a7a94
[-] start the builder container ...
go: downloading github.com/tidwall/gjson v1.14.3
go: downloading github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230829022308-8747e1ddadf0
go: downloading github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
go: downloading github.com/stretchr/testify v1.8.0
go: downloading github.com/tidwall/match v1.1.1
go: downloading github.com/tidwall/pretty v1.2.0
go: downloading github.com/google/uuid v1.3.0
go: downloading github.com/wasilibs/nottinygc v0.3.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/magefile/mage v1.14.0
[√] finish building!

查看构建产物:

$ tree -a out/
out/
├── README.md
├── plugin.wasm
└── spec.yaml

0 directories, 3 files

(1) spec.yaml

spec.yaml 作为插件的 metadata,其中 spec.configSchema.openAPIV3Schema 用于校验配置参数,在 install 命令中会用到:

apiVersion: 1.0.0
info:
  category: custom
  name: hello-world
  title: Hello World
  x-title-i18n:
    zh-CN: Hello World
  description: This is a demo plugin
  x-description-i18n:
    zh-CN: This is a demo plugin
  version: 0.1.0
  contact:
    name: WeixinX
    url: www.github.com/weixinx
    email: [email protected]
spec:
  phase: UNSPECIFIED_PHASE
  priority: 10
  configSchema:
    openAPIV3Schema:
      type: object
      required:
        - firstField
        - secondField
      properties:
        firstField:
          type: string
          title: 第一个字段,注解格式为 @Title [语言] [标题],语言缺省值为 zh-CN
          x-title-i18n:
            zh-CN: 第一个字段,注解格式为 @Title [语言] [标题],语言缺省值为 zh-CN
          description: 字符串的前半部分,注解格式为 @Description [语言] [描述],语言缺省值为 zh-CN
          x-description-i18n:
            zh-CN: 字符串的前半部分,注解格式为 @Description [语言] [描述],语言缺省值为 zh-CN
        secondField:
          type: string
          title: Second Field, annotation format is @Title [language] [title], language defaults to zh-CN
          x-title-i18n:
            en-US: Second Field, annotation format is @Title [language] [title], language defaults to zh-CN
          description: The second half of the string, annotation format is @Description [language] [description], language defaults to zh-CN
          x-description-i18n:
            en-US: The second half of the string, annotation format is @Description [language] [description], language defaults to zh-CN
      example:
        firstField: hello
        secondField: world

(2) README.md

> 该插件用法文件根据源代码自动生成,请根据需求自行修改!

# 功能说明

This is a demo plugin

# 配置字段

| 名称 | 数据类型 | 填写要求 |  默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
| firstField | string | 必填 | - | 字符串的前半部分,注解格式为 @Description [语言] [描述],语言缺省值为 zh-CN |
| secondField | string | 必填 | - |  |

# 配置示例

```yaml
firstField: hello
secondField: world
```

2. image: 将构建产物作为 OCI 镜像上传到镜像仓库(用于安装)

首先使用 docker login 登录,构建容器中会复用这个登录凭证。再使用 --output-type image --output-dest docker.io/weixinx/hello-world:v0.1.0 覆盖 option.yaml 的选项:

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: weixinx
Password:
WARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

$ hgctl plugin build --model HelloWorldConfig --output-type image --output-dest docker.io/weixinx/hello-world:v0.1.0
[-] pull the builder image ...
{"status":"Pulling from plugins/wasm-go-builder","id":"go1.19-tinygo0.28.1-oras1.0.0"}
{"status":"Digest: sha256:9b5a104f4e6bca9fc102a3efc835a2136a8eaa4b76cafbbf93a71098570896c1"}
{"status":"Status: Image is up to date for higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0"}
[√] pull the builder image: higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0
[-] create the builder container ...
[√] create the builder container: 24f42f1fcc04d2445bb5943bde68e52e7cd59356a857eb7e7c1160bb06e4164d
[-] start the builder container ...
go: downloading github.com/tidwall/gjson v1.14.3
go: downloading github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230829022308-8747e1ddadf0
go: downloading github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
go: downloading github.com/tidwall/match v1.1.1
go: downloading github.com/tidwall/pretty v1.2.0
go: downloading github.com/google/uuid v1.3.0
go: downloading github.com/wasilibs/nottinygc v0.3.0
go: downloading github.com/stretchr/testify v1.8.0
go: downloading github.com/magefile/mage v1.14.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.1
Uploading 9b667d2da913 plugin.tar.gz
Uploading 39e6ef91f5a3 README.md
Uploading a2b3ef03bd79 spec.yaml
Uploaded  39e6ef91f5a3 README.md
Uploaded  a2b3ef03bd79 spec.yaml
Uploaded  9b667d2da913 plugin.tar.gz
Pushed [registry] docker.io/weixinx/hello-world:v0.1.0
Digest: sha256:3eaf58070e6b504293dcad90f03430e6ec4fed4e7772e4a8bad01f3545f6bb5f
[√] finish building and pushing!

3.4. hgctl plugin test

test 命令拥有 5 个子命令:

  1. create: 创建测试环境,即生成 docker-compose.yaml, envoy.yaml 和 plugin-conf.yaml;
  2. start: 启动测试环境,类似于 docker compose up
  3. stop: 关停测试环境,类似于 docker compose down
  4. clean:清理测试环境,除了关停测试环境外,还将生成的测试文件目录删除;
  5. ls: 查看已启动的测试环境。

3.4.1. 命令选项

1. hgctl plugin test create

$ hgctl plugin test create -h
Create the test environment

Usage:
  hgctl plugin test create [flags]

Aliases:
  create, c

Examples:
  # If the option.yaml file exists in the current path, do the following:
  hgctl plugin test create

  # Explicitly specify the source of the parameters (directory of the build
    products) and the directory where the test configuration files is stored
  hgctl plugin test create -d ./out -t ./test


Flags:
  -d, --from-path string     Path of storing the build products (default "./out")
  -h, --help                 help for create
  -f, --option-file string   Option file for build, test and install (default "./option.yaml")
  -t, --test-path string     Path for storing the test configuration (default "./test")

2. hgctl plugin test start

$ hgctl plugin test start -h
Start the test environment

Usage:
  hgctl plugin test start [flags]

Aliases:
  start, s

Examples:
  # If the option.yaml file exists in the current path, do the following:
  hgctl plugin test start

  # Run containers in the background with the option --detach(-d)
  hgctl plugin test start -d


Flags:
  -c, --compose-file string   Docker compose configuration file
  -d, --detach                Detached mode: Run containers in the background
  -h, --help                  help for start
  -p, --name string           Test environment name (default "wasm-test")
  -f, --option-file string    Option file for build, test and install (default "./option.yaml")
  -t, --test-path string      Test configuration source (default "./test")

3. hgctl plugin test stop

$ hgctl plugin test stop -h
Stop the test environment

Usage:
  hgctl plugin test stop [flags]

Aliases:
  stop, st

Examples:
  # Stop responding to the compose containers with the option --name(-p)
  hgctl plugin test stop -p wasm-test


Flags:
  -h, --help                 help for stop
  -p, --name string          Test environment name (default "wasm-test")
  -f, --option-file string   Option file for build, test and install (default "./option.yaml")

4. hgctl plugin test clean

$ hgctl plugin test clean -h
Clean the test environment, that is remove the source of test configuration

Usage:
  hgctl plugin test clean [flags]

Aliases:
  clean, c

Examples:
  hgctl plugin test clean

Flags:
  -h, --help                 help for clean
  -p, --name string          Test environment name (default "wasm-test")
  -f, --option-file string   Option file for build, test and install (default "./option.yaml")
  -t, --test-path string     Test configuration source (default "./test")

5. hgctl plugin test ls

$ hgctl plugin test ls -h
List all test environments

Usage:
  hgctl plugin test ls [flags]

Aliases:
  ls, l

Examples:
  hgctl plugin test ls

Flags:
  -h, --help   help for ls

3.4.2. 使用示例

以前文 Hello World 插件为例:

$ pwd
/home/ubuntu/hello-world

$ ls
go.mod  go.sum  main.go  option.yaml  out

1. create: 创建测试环境

由于前面已经将构建产物输出到本地,因此只需依赖 option.yaml 的默认选项便可创建测试环境:

$ hgctl plugin test create
Created the test environment in "/home/ubuntu/hello-world/test"

$ ls test/
docker-compose.yaml  envoy.yaml  plugin-conf.yaml

这些生成的配置文件均使用 spec.yaml 文件中对应参数进行填充:

(1) docker-compose.yaml

# File generated by hgctl. Modify as required.

version: '3.7'
services:
  envoy:
    image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/envoy:1.20
    command: envoy -c /etc/envoy/envoy.yaml --component-log-level wasm:debug
    depends_on:
    - httpbin
    networks:
    - wasmtest
    ports:
    - "10000:10000"
    volumes:
    - /home/ubuntu/hello-world/test/envoy.yaml:/etc/envoy/envoy.yaml
    - /home/ubuntu/hello-world/out/plugin.wasm:/etc/envoy/plugin.wasm

  httpbin:
    image: kennethreitz/httpbin:latest
    networks:
    - wasmtest
    ports:
    - "12345:80"

networks:
  wasmtest: {}

(2) envoy.yaml

# File generated by hgctl. Modify as required.

admin:
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address:
          protocol: TCP
          address: 0.0.0.0
          port_value: 10000
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                scheme_header_transformation:
                  scheme_to_overwrite: https
                stat_prefix: ingress_http
                # Output envoy logs to stdout
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                # Modify as required
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: httpbin
                http_filters:
                  - name: wasmtest
                    typed_config:
                      "@type": type.googleapis.com/udpa.type.v1.TypedStruct
                      type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
                      value:
                        config:
                          name: wasmtest
                          vm_config:
                            runtime: envoy.wasm.runtime.v8
                            code:
                              local:
                                filename: /etc/envoy/plugin.wasm
                          configuration:
                            "@type": "type.googleapis.com/google.protobuf.StringValue"
                            # Modify as required
                            value: |
                              {
                                "firstField": "hello",
                                "secondField": "world"
                              }
                  - name: envoy.filters.http.router
  clusters:
    - name: httpbin
      connect_timeout: 30s
      type: LOGICAL_DNS
      # Comment out the following line to test on v6 networks
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: httpbin
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: httpbin
                      port_value: 80

(3) plugin-conf.yaml

实际上在启动测试环境中并没有使用到该文件,该文件交由用户在测试集群中按需使用:

# File generated by hgctl. Modify as required.
# See: https://higress.io/zh-cn/docs/plugins/intro

apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: hello-world
  namespace: higress-system
  annotations:
    higress.io/wasm-plugin-title: Hello World
    higress.io/wasm-plugin-description: This is a demo plugin
    higress.io/wasm-plugin-icon: https://img.alicdn.com/imgextra/i1/O1CN018iKKih1iVx287RltL_!!6000000004419-2-tps-42-42.png
  labels:
    higress.io/wasm-plugin-name: hello-world
    higress.io/wasm-plugin-category: custom
    higress.io/wasm-plugin-version: 0.1.0
    higress.io/resource-definer: higress
    higress.io/wasm-plugin-built-in: "false"
spec:
  phase: UNSPECIFIED_PHASE
  priority: 10
  defaultConfig:
    firstField: hello
    secondField: world

  # Please fill the image url in according to your needs
  url:

2. start & ls: 启动并查看测试环境

在此以后台运行的方式(--detach)启动测试环境:

$ hgctl plugin test start --detach
[+] Running 2/2
 ⠿ Container wasm-test-httpbin-1  Started                        2.5s
 ⠿ Container wasm-test-envoy-1    Started                        3.8s
Started the test environment "wasm-test"

查看已启动的测试环境:

$ hgctl plugin test ls
NAME        STATUS       CONFIG FILES
wasm-test   running(2)   /home/ubuntu/hello-world/test/docker-compose.yaml

测试 Hello World 插件:

# 请求 envoy 代理
$ curl -v 127.0.0.1:10000/get
...
* Connected to 127.0.0.1 (127.0.0.1) port 10000 (#0)
> GET /get HTTP/1.1
> Host: 127.0.0.1:10000
...
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< server: envoy
...
<
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    **"Hello": "world",** # 插件的功能
    ...
  },
  ...
}
* Connection #0 to host 127.0.0.1 left intact

3. stop: 关停测试环境

$ hgctl plugin test stop
[+] Running 3/3
 ⠿ Container wasm-test-envoy-1    Removed                        1.7s
 ⠿ Container wasm-test-httpbin-1  Removed                        2.3s
 ⠿ Network wasm-test_wasmtest     Removed                        0.4s
Stopped the test environment "wasm-test"

4. clean: 清理测试环境

由于上面已经关停了测试环境,因此清理命令只是移除了 test 目录

$ hgctl plugin test clean
Warning: No resource found to remove for project "wasm-test".
Stopped the test environment "wasm-test"
Removed the source "/home/ubuntu/hello-world/test"

$ ls
go.mod  go.sum  main.go  option.yaml  out

3.5. hgctl plugin install/uninstall/ls

  1. install: 用于安装 WASM 插件,支持使用 yaml 文件和 WASM 项目源文件两种安装方式。
  2. uninstall: 卸载 WASM 插件。
  3. ls: 查看已安装的 WASM 插件。

3.5.1. 命令选项

1. hgctl plugin install

$ hgctl plugin install -h
Install WASM plugin

Usage:
  hgctl plugin install [flags]

Aliases:
  install, i, ins

Examples:
  # Install WASM plugin using a WasmPlugin manifest
  hgctl plugin install -y plugin-conf.yaml

  # Install WASM plugin through the Golang WASM plugin project
  docker login
  hgctl plugin install -g ./


Flags:
      --context string           The name of the kubeconfig context to use.
      --debug                    Enable debug mode
  -g, --from-go-project string   Install WASM plugin through the Golang WASM plugin project
  -y, --from-yaml string         Install WASM plugin using a WasmPlugin manifest (default "./test/plugin-conf.yaml")
  -h, --help                     help for install
      --kubeconfig string        Path to the kubeconfig file to use for CLI requests.
  -n, --namespace string         Namespace where Higress was installed (default "higress-system")
  -f, --option-file string       Option file for build, test and install (default "./option.yaml")
  -s, --spec-yaml string         Use to validate WASM plugin configuration (default "./out/spec.yaml")

2. hgctl plugin uninstall

$ hgctl plugin uninstall -h
Uninstall WASM plugin

Usage:
  hgctl plugin uninstall [flags]

Aliases:
  uninstall, u, uins

Examples:
  # Uninstall WASM plugin using the WasmPlugin name
  hgctl plugin uninstall -p example-plugin-name

  # Uninstall all WASM plugins
  hgctl plugin uninstall -A


Flags:
  -A, --all                 Delete all installed WASM plugin
      --context string      The name of the kubeconfig context to use.
  -h, --help                help for uninstall
      --kubeconfig string   Path to the kubeconfig file to use for CLI requests.
  -p, --name string         Name of the WASM plugin you want to uninstall
  -n, --namespace string    Namespace where Higress was installed (default "higress-system")

3. hgctl plugin ls

$ hgctl plugin ls -h
List all installed WASM plugins

Usage:
  hgctl plugin ls [flags]

Aliases:
  ls, l

Examples:
hgctl plugin ls

Flags:
      --context string      The name of the kubeconfig context to use.
  -h, --help                help for ls
      --kubeconfig string   Path to the kubeconfig file to use for CLI requests.
  -n, --namespace string    Namespace where Higress was installed (default "higress-system")

3.5.2. 使用示例

1. install: 安装插件

依然使用上述的 Hello World 插件为例,介绍两种安装方式:

(2) --from-yaml: 根据 yaml 文件安装

首先还原前面清除的测试环境:

$ hgctl plugin test create
Created the test environment in "/home/ubuntu/hello-world/test"

修改 test/plugin-conf.yaml 文件的 OCI 镜像 URL 为 build 命令处的镜像目标:

$ vim test/plugin-conf.yaml

# File generated by hgctl. Modify as required.
# See: https://higress.io/zh-cn/docs/plugins/intro

apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: hello-world
  namespace: higress-system
  annotations:
    ...
spec:
  phase: UNSPECIFIED_PHASE
  priority: 10
  defaultConfig:
    firstField: hello
    secondField: world

  # Please fill the image url in according to your needs
  **url: docker.io/weixinx/hello-world:v0.1.0**

依赖 option.yaml 默认选项进行安装:

$ hgctl plugin install
Installed wasm plugin "higress-system/hello-world"

(2) --from-go-project: 根据项目源文件安装

这种方式会首先构建插件项目文件,并将构建结果作为 OCI 镜像上传,再通过命令行交互让用户输入插件配置,最终根据配置进行安装。

该方式实际上就是 build 命令将产物以 OCI 镜像输出,再利用自动生成 plugin-conf.yaml 进行安装,因此需要修改 option.yaml 文件的 build 和 install 选项:

$ vim option.yaml

build:
  ...
  output:
  # Choose between 'files' and 'image'
    **type: image**
    # Destination address: when type=files, specify the local directory path, e.g., './out' or
    # type=image, specify the remote docker repository, e.g., 'docker.io/<your_username>/<your_image>'
    **dest: docker.io/weixinx/hello-world:v0.1.0**
  ...

test:
  ...

install:
  ...
  # If 'from-go-project' is non-empty, the output type of the build option must be 'image'.
  **from-go-project: ./**
  ...

执行安装命令(前面已经安装过同样的插件了,所以会报告已存在):

$ hgctl plugin install
[-] pull the builder image ...
{"status":"Pulling from plugins/wasm-go-builder","id":"go1.19-tinygo0.28.1-oras1.0.0"}
{"status":"Digest: sha256:9b5a104f4e6bca9fc102a3efc835a2136a8eaa4b76cafbbf93a71098570896c1"}
{"status":"Status: Image is up to date for higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0"}
[√] pull the builder image: higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0
[-] create the builder container ...
[√] create the builder container: b0ee40cfad0fee05b7330acdab536a6f74afb49cf629d42cdbd52ecf5eba885b
[-] start the builder container ...
go: downloading github.com/tidwall/gjson v1.14.3
go: downloading github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230829022308-8747e1ddadf0
go: downloading github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
go: downloading github.com/tidwall/match v1.1.1
go: downloading github.com/tidwall/pretty v1.2.0
go: downloading github.com/google/uuid v1.3.0
go: downloading github.com/wasilibs/nottinygc v0.3.0
go: downloading github.com/stretchr/testify v1.8.0
go: downloading github.com/magefile/mage v1.14.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.1
Uploading bb05515f156c plugin.tar.gz
Uploaded  bb05515f156c plugin.tar.gz
Exists    39e6ef91f5a3 README.md
Exists    a2b3ef03bd79 spec.yaml
Pushed [registry] docker.io/weixinx/hello-world:v0.1.0
Digest: sha256:d4edde2080841749b7ac625ad460826e66531dc7aab6155fdec644426948db49
[√] finish building and pushing!
Please enter the configurations for the WASM plugin you want to install:
Configuration example:

firstField: hello
secondField: world

? Choose a configuration effective scope or complete: INSTANCE
? Choose Ingress or Domain: Ingress
? Enter the matched ingress: foo
? continue? No
  Ingress:
    HelloWorldConfig(object, required)
?     firstField(string, required) ni
?     secondField(string, required) hao
√ Successful to add configuration.
? Choose a configuration effective scope or complete: Complete
The complete configuration is as follows:

matchRules:
  - ingress:
      - foo
    config:
      firstField: ni
      secondField: hao

wasm plugin "higress-system/hello-world" already exists

2. ls: 查看已安装插件

$ hgctl plugin ls
NAME          AGE
hello-world   5m

3. uninstall: 卸载插件

$ hgctl plugin uninstall --name hello-world
Uninstalled wasm plugin "higress-system/hello-world"

$ hgctl plugin ls
NAME   AGE

3.6. hgctl plugin config

config 命令有两个子命令:

  1. create: 创建 plugin-conf.yaml 插件配置模板文件;
  2. edit: 编辑以安装的插件配置

3.6.1. 命令选项

hgctl plugin config -h
Configure the WasmPlugin manifest

Usage:
  hgctl plugin config [command]

Aliases:
  config, c, conf

Available Commands:
  create      Create the WasmPlugin configuration template file
  edit        Edit the installed WasmPlugin configuration, similar to `kubectl edit`

Flags:
  -h, --help   help for config

3.6.2. 使用示例

为了编辑已安装的插件配置,我们还原上面被卸载的 Hello World 插件。

$ hgctl plugin install -y test/plugin-conf.yaml
Installed wasm plugin "higress-system/hello-world"

$ hgctl plugin ls
NAME          AGE
hello-world   10s

执行 edit 命令修改配置,与kubectl edit类似,通过编辑器编辑配置后退出即可:

$ hgctl plugin config edit --name hello-world

apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  annotations:
    higress.io/wasm-plugin-description: This is a demo plugin
    higress.io/wasm-plugin-icon: https://img.alicdn.com/imgextra/i1/O1CN018iKKih1iVx287RltL_!!6000000004419-2-tps-42-42.png
    higress.io/wasm-plugin-title: Hello World
  creationTimestamp: "2023-09-07T08:06:08Z"
  generation: 1
  labels:
    higress.io/resource-definer: higress
    higress.io/wasm-plugin-built-in: "false"
    higress.io/wasm-plugin-category: custom
    higress.io/wasm-plugin-name: hello-world
    higress.io/wasm-plugin-version: 0.1.0
  name: hello-world
  namespace: higress-system
  resourceVersion: "235519"
  uid: 8f74e9dc-868f-44d5-a219-c18ee8baa733
spec:
  defaultConfig:
    firstField: ni
    secondField: hao
  phase: UNSPECIFIED_PHASE
  priority: 10
  url: docker.io/weixinx/hello-world:v0.1.0
~
~
~
:wq

Edited wasm plugin "higress-system/hello-world"

@codecov-commenter
Copy link

codecov-commenter commented Jul 27, 2023

Codecov Report

Merging #453 (8d8cd2c) into main (4a5127f) will decrease coverage by 1.72%.
The diff coverage is 19.24%.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #453      +/-   ##
==========================================
- Coverage   37.46%   35.74%   -1.72%     
==========================================
  Files          65       70       +5     
  Lines        9215    10114     +899     
==========================================
+ Hits         3452     3615     +163     
- Misses       5497     6213     +716     
- Partials      266      286      +20     
Files Coverage Δ
pkg/cmd/hgctl/root.go 0.00% <0.00%> (ø)
pkg/cmd/hgctl/plugin/types/model_parser.go 69.20% <69.20%> (ø)
pkg/cmd/hgctl/plugin/types/annotation.go 0.00% <0.00%> (ø)
pkg/cmd/hgctl/plugin/types/marshal.go 0.00% <0.00%> (ø)
pkg/cmd/hgctl/plugin/types/meta.go 0.00% <0.00%> (ø)
pkg/cmd/hgctl/plugin/types/schema.go 0.00% <0.00%> (ø)

... and 1 file with indirect coverage changes

@WeixinX WeixinX changed the title feat: add command "hgctl plugin build" Record the progress of the OSPP 2023 hgctl project Jul 31, 2023
Copy link
Collaborator

@Xunzhuo Xunzhuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not look through all, but it looks good, plz resolve the conflicts

@WeixinX
Copy link
Collaborator Author

WeixinX commented Sep 26, 2023

The conflicts have been resolved.

pkg/cmd/hgctl/kubernetes/wasmplugin.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/kubernetes/wasmplugin.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/kubernetes/wasmplugin.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/plugin/build/templates.go Outdated Show resolved Hide resolved
md.Close()
return errors.Wrap(err, "failed to execute README.md or README_{lang}.md template")
}
md.Close()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

类似的地方要用 defer

for _, item := range list.Items {
fmt.Fprintf(printer, "%s\t%s\n", item.GetName(), getAge(item.GetCreationTimestamp().Time))
}
printer.Flush()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ErrorHandling 待优化

pkg/cmd/hgctl/plugin/plugin.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/plugin/uninstall/uninstall.go Outdated Show resolved Hide resolved
@WeixinX WeixinX marked this pull request as ready for review October 6, 2023 08:58
@johnlanni
Copy link
Collaborator

@WeixinX hgctl plugin init 可以生成 option.yml,是否在init时让用户输入镜像仓库地址,这样无需再修改option.yml

@WeixinX
Copy link
Collaborator Author

WeixinX commented Oct 7, 2023

@johnlanni 因为我的想法是默认构建产物为文件形式,所以 option.yaml 里的 output.type 默认为 files, output.dest 也就是本地目录,而不是镜像仓库地址了。或者考虑把这两个选项分开也可以的

@johnlanni
Copy link
Collaborator

@johnlanni 因为我的想法是默认构建产物为文件形式,所以 option.yaml 里的 output.type 默认为 files, output.dest 也就是本地目录,而不是镜像仓库地址了。或者考虑把这两个选项分开也可以的

我的出发点是希望plugin install时不需要再去修改option.yaml,延伸开来看,其实是希望80%的普通简单场景,可以在plugin init时基于的输入参数生成option.yaml后,都不需要再调整这份自动生成的option.yaml。从而简化用户使用。

另外,我建议命令行参数和option.yaml不要一一对应,有了option.yaml后,应该简化命令行参数,让命令行参数专注于action(例如 build to image 或 build to file),其他配置都让option.yaml来做

@johnlanni
Copy link
Collaborator

现在 hgctl plugin init 生成代码中的 model config 的名称是根据插件名自动生成的么,例如 hello-world 变成 HelloWorldConfig ,这里我建议 model config 的名称可以是固定的,例如 PluginConfig,这样 -s 这个参数可以有默认值,无需用户配置。

Copy link
Collaborator

@CH3CHO CH3CHO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

没啥大问题。下面的评论可改可不改。

pkg/cmd/hgctl/plugin/build/templates.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/plugin/install/asker.go Show resolved Hide resolved
pkg/cmd/hgctl/plugin/install/asker.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/plugin/install/asker.go Outdated Show resolved Hide resolved
pkg/cmd/hgctl/plugin/install/asker.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@Xunzhuo Xunzhuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, let us get this in.

@johnlanni johnlanni merged commit 901ad96 into alibaba:main Oct 27, 2023
9 checks passed
@WeixinX WeixinX deleted the feat/ospp-hgctl branch October 27, 2023 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants