Skip to content

Latest commit

 

History

History
130 lines (100 loc) · 4.44 KB

plugin.md

File metadata and controls

130 lines (100 loc) · 4.44 KB

Plugin system

falco provides plugin system by calling external CLI command.

Custom Linter Execution

Sometimes you want to write your own linter rule, and then you can do it by implementing CLI command.

Plugin Implementation

Your plugin needs to implement binary messaging protocol between stdin and stdout/stderr. falco provided plugin package that can implement them so you can use it to make plugin.

Also, we have example plugin implementation here, this is custom linter rule that the backend name must have F_ prefix.

// main.go
package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/ysugimoto/falco/ast"
	"github.com/ysugimoto/falco/plugin"
)

func main() {
	// Read from stdin and decode AST tree struct from main falco linter.
	// Note that this function needs generics, it specified type conversion of provided statement.
	// In this case, linting for *ast.BackendDeclaration object.
	req, err := plugin.ReadLinterRequest[*ast.BackendDeclaration](os.Stdin)
	if err != nil {
 		// If some error has occured, send back message to stderr
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	// Prepare send back response message
	resp := &plugin.LinterResponse{}

	// Main linting logic, the backend name must have "F_" prefix
	// By using generics, req.Statement could be *ast.BackendDeclaration pointer.
	if !strings.HasPrefix(req.Statement.Name.Value, "F_") {
 		// Report ERROR severity in linter
		resp.Error(`Backend name must start with "F_"`)

		// Or, report WARNING severity in linter
		// resp.Warning(`Backend name must start with "F_"`)

 		// Or, report INFO severity in linter
		// resp.Info(`Backend name must start with "F_"`)
	}

	// Send back result message to stdout including some linting errors
	resp.Write(os.Stdout)
}

After that, you can build CLI binary with having falco- prefix:

go build -o falco-backend-name .

And put built binary in your $PATH as executable.

Put instruction to call plugin in VCL

falco recognizes @plugin annotation to call plugin linter. To call above backend name linter, put leading comment on backend declaration.

// @plugin: backend-name
backend example_com {
  .connect_timeout = 1s;
  .dynamic = true;
  .port = "443";
  .host = "httpbin.org";
  .first_byte_timeout = 20s;
  .max_connections = 500;
  .between_bytes_timeout = 20s;
  .share_key = "xei5lohleex3Joh5ie5uy7du";
  .ssl = true;
  .ssl_sni_hostname = "example.com";
  .ssl_cert_hostname = "example.com";
  .ssl_check_cert = always;
  .min_tls_version = "1.2";
  .max_tls_version = "1.2";
  .bypass_local_route_table = false;
  .probe = {
    .request = "GET / HTTP/1.1" "Host: example.com" "Connection: close";
    .dummy = true;
    .threshold = 1;
    .window = 2;
    .timeout = 5s;
    .initial = 1;
    .expected_response = 200;
    .interval = 10s;
  }
}

...(other declarations)

And then you can run custom plugin linter via falco linter. In this case, the backend nake does not have F_ prefix so linter reports ERROR on linting results.

Additionally, you can put additional arguments to plugin:

// @plugin: backend-name arg1 arg2 ...

And then you can receive these arguments via (*plugin.LinterRequest).Arguments as string slice:

req, err := plugin.ReadLinterRequest[*ast.BackendDeclaration](os.Stdin)
fmt.Println(req.Arguments[0]) // arg1
fmt.Println(req.Arguments[1]) // arg2

This is useful for switch or control linting behavior in your plugin without implementing another plugin.

Other Specs

  1. To reduce binary message between falco linter and plugin, we enable custom linter for each statement only. You often want to access all declarations on your plugin, but it cannot do for now. Therefore if you want to do linting for all backends, you need to put annotation comment to all backend declarations.
  2. From performance reason, we omit AST meta informations like filename, line number and position and all comments. Typically you don't need to read AST meta informations on linting but the falco main process displays plugin error with AST meta informations.
  3. AST is read-only on the plugin so your plugin cannot modify AST tree.
  4. You can everythin in your plugin. It means you can do network access, reading local file, etc... that as possible as the programming language can if you don't mind the linting performance.
  5. Binary messsaging is just raw binary treating so you can implement plugin not only Go but also other languages that can process binary.