中文

table of contents

check dependencies

if you have dependencies on external libraries , check the dependent items . if your plugin needs to use shared memory , it needs to declare in bin/apisix , for example :

    lua_shared_dict plugin-limit-req     10m;
    lua_shared_dict plugin-limit-count   10m;
    lua_shared_dict prometheus-metrics   10m;
    lua_shared_dict plugin-limit-conn    10m;
    lua_shared_dict upstream-healthcheck 10m;
    lua_shared_dict worker-events        10m;

    # for openid-connect plugin
    lua_shared_dict discovery             1m; # cache for discovery metadata documents
    lua_shared_dict jwks                  1m; # cache for JWKs
    lua_shared_dict introspection        10m; # cache for JWT verification results

The plugin itself provides the init method . It is convenient for plugins to perform some initialization after the plugin is loaded .

Note : if the dependency of some plugin needs to be initialized when Nginx start , you may need to add logic to the initialization method “http_init” in the file Lua/apifix.lua , And you may need to add some processing on generated part of Nginx configuration file in bin/apisix file . but it is easy to have an impact on the overall situation according to the existing plugin mechanism, we do not recommend this unless you have a complete grasp of the code .

name and config

determine the name and priority of the plugin , and add to conf/config.yaml . For example , for the key-auth plugin , you need to specify the plugin name in the code (the name is the unique identifier of the plugin and cannot be duplicate) , you can see the code in file “lua/apisix/plugins/key-auth.lua” :

   local plugin_name = "key-auth"

   local _M = {
      version = 0.1,
      priority = 2500,
      type = 'auth',
      name = plugin_name,
      schema = schema,
   }

Note : The priority of the new plugin cannot be the same as the priority of any existing plugin.

in the “conf/config.yaml” configuration file , the enabled plugins (all specified by plugin name) are listed .

plugins:                          # plugin list
  - example-plugin
  - limit-req
  - limit-count
  - limit-conn
  - key-auth
  - prometheus
  - node-status
  - jwt-auth
  - zipkin
  - ip-restriction
  - grpc-transcode
  - serverless-pre-function
  - serverless-post-function
  - openid-connect
  - proxy-rewrite
  - redirect

Note : the order of the plugins is not related to the order of execution .

schema and check

Write Json Schema descriptions and check functions. similarly , take the key-auth plugin as an example to see its configuration data :

 "key-auth" : {
       "key" : "auth-one"
  }

The configuration data of the plugin is relatively simple . Only one attribute named key is supported . Let's look at its schema description :

   local schema = {
       type = "object",
       properties = {
           key = {type = "string"},
       }
   }

at the same time, we need to implement the check_schema(conf) method to complete the specification verification .

   function _M.check_schema(conf)
       return core.schema.check(schema, conf)
   end

Note: the project has provided the public method “core.schema.check” , which can be used directly to complete JSON verification .

choose phase to run

determine which phase to run , generally access or rewrite . if you don‘t know the Openresty life cycle , it’s recommended to know it in advance . key-auth is an authentication plugin , as long as the authentication is completed before the business response after the request comes in . The plugin can be executed in the rewrite and access phases , in the project, the authentication logic is implemented in the rewrite phase . Generally, IP access and interface permission are completed in the access phase .

implement the logic

Write the logic of the plugin in the corresponding phase .

write test case

for functions , write and improve the test cases of various dimensions , do a comprehensive test for your plugin ! The test cases of plugins are all in the “t/plugin” directory. You can go ahead to find out . the test framework test-nginx adopted by the project. a test case, .t file is usually divided into prologue and data parts by _data_ . Here we will briefly introduce the data part, that is, the part of the real test case . For example, the key-auth plugin :

=== TEST 1: sanity
--- config
    location /t {
        content_by_lua_block {
            local plugin = require("apisix.plugins.key-auth")
            local ok, err = plugin.check_schema({key = 'test-key'})
            if not ok then
                ngx.say(err)
            end

            ngx.say("done")
        }
    }
--- request
GET /t
--- response_body
done
--- no_error_log
[error]

a test case consists of three parts :

  • Program code : configuration content of Nginx location
  • Input : http request information
  • Output check : status, header, body, error log check

when we request /t , which config in the configuration file , the Nginx will call “content_by_lua_block” instruction to complete the Lua script, and finally return. The assertion of the use case is response_body return “done”, “no_error_log” means to check the “error.log” of Nginx. There must be no ERROR level record .

Attach the test-nginx execution process:

According to the path we configured in the makefile and some configuration items at the front of each .t file, the framework will assemble into a complete nginx.conf file. “t/servroot” is the working directory of Nginx and start the Nginx instance. according to the information provided by the test case, initiate the http request and check that the return items of HTTP include HTTP status, HTTP response header, HTTP response body and so on .