Gonginx
repository·master·Indexed 42 days ago
https://github.com/tufanbarisyildirim/gonginxA Go library for parsing, manipulating, and regenerating Nginx configuration files. It provides a lexer and parser to convert Nginx configs into Go objects (AST), allowing developers to programmatically manage settings such as load balancer upstreams, server blocks, and listen ports.
What's inside Gonginx
- Gonginx is an Nginx configuration parser designed for Go applications. It allows developers to parse, edit, and regenerate Nginx configuration files programmatically. This is particularly useful for managing dynamic balancer configurations within a Go-based infrastructure.
Understand Parent-Child Directive Relationships
masterGonginx maintains a hierarchical structure in its AST (Abstract Syntax Tree) to allow for easier manipulation of nested configurations:
- Root-level directives: Leaf directives located at the root of the configuration have a
nilparent. - Nested directives: Every nested directive points to its enclosing directive wrapper (for example, a
locationdirective will point to its enclosingserverdirective, which in turn points tohttp).
- Root-level directives: Leaf directives located at the root of the configuration have a
Configure Include Parsing behavior
masterBy default, Gonginx handles includes. You can customize this behavior using parser options:
- Enable include parsing: Use
parser.WithIncludeParsing(). - Include deduplication: Includes are automatically deduplicated by their canonical file path.
- Cycle detection: Cyclic include branches are skipped by default to prevent infinite recursion. To return an explicit error when a cycle is detected, use
parser.WithIncludeCycleErr().
- Enable include parsing: Use
Core Components of Gonginx
masterGonginx is composed of three primary functional areas:
- Parser: The main engine that analyzes Nginx structured files and converts them into Go objects. It utilizes a
lexerto generatetokens and aparserto transform those tokens into configuration objects. - Config: Provides the Go representations (models and AST) for any context, directive, or parameter within an Nginx configuration.
- Dumper: Handles the styling and formatting configuration for regenerating (dumping) the configuration files.
- Parser: The main engine that analyzes Nginx structured files and converts them into Go objects. It utilizes a
How Upstream Lookup modes work
masterWhen searching for upstream blocks, Gonginx offers two modes:
FindUpstreams(): Permissive mode. It skips unexpected types and continues searching.FindUpstreamsStrict(): Strict mode. It returns a typed error if it encounters unexpected upstream directive types.
Parse an Nginx configuration string
masterTo parse Nginx configuration content provided as a string, use
parser.NewStringParser(fullConf)followed by the.Parse()method. This is useful for manipulating configuration snippets in memory.p := parser.NewStringParser(fullConf) conf, err := p.Parse() if err != nil { panic(err) }Dump Nginx configuration to a string or file
masterGonginx provides two ways to regenerate Nginx configuration from a
config.Configobject:- To a string: Use
dumper.DumpConfig(conf, style). Usedumper.IndentedStylefor human-readable output. - To a file: Use
dumper.WriteConfig(conf, style, overwrite). You can set the target path on the config object itself usingconf.FilePath = filePathbefore calling this.
Available styles include
dumper.IndentedStyle.// Dump to string dumpString := dumper.DumpConfig(conf, dumper.IndentedStyle) // Dump to file conf.FilePath = "/path/to/nginx.conf" dumper.WriteConfig(conf, dumper.IndentedStyle, false)- To a string: Use
Parse an Nginx configuration file
masterTo parse an existing Nginx configuration file from disk, use
parser.NewParser(filePath)followed by the.Parse()method. This returns aconfig.Configobject representing the parsed structure.func parseConfigAndGetPorts(filePath string) ([]string, error) { p, err := parser.NewParser(filePath) if err != nil { return nil, fmt.Errorf("failed to create parser: %w", err) } conf, err := p.Parse() if err != nil { return nil, fmt.Errorf("failed to parse config: %w", err) } // ... use conf to find directives }Configure Dumper Styling and Sorting
masterThe
dumperpackage manages how the configuration is written back to a file.- Non-mutating Sort: Using
dumper.NoIndentSortedStyleor other sorted dump options will sort the output, but it will not mutate the order of directives in the in-memory AST. This allows you to maintain a specific logical order in your code while producing a formatted output.
- Non-mutating Sort: Using
Configure Dumper output style
masterUse the
Stylestruct to control the formatting of the dumped configuration. You can use predefined styles or create a custom one.Predefined Styles:
NoIndentStyle: No indentation, no sorting.IndentStyle: Indentation of 4 spaces.NoIndentSortedStyle: No indentation, but directives are sorted.NoIndentSortedSpaceStyle: No indentation, directives are sorted, and spaces are added before blocks.
Customization Fields:
SortDirectives bool: Whether to sort directives.SpaceBeforeBlocks bool: Whether to add space before blocks.StartIndent int: Initial indentation level.Indent int: Indentation level for nested elements.Debug bool: Enable debug mode.DisableLuaFormatting bool: Disable Lua-specific formatting.LuaFormatter LuaFormatterFunc: Custom function for formatting Lua blocks.
Style Methods:
WithLuaFormatting(enabled bool) *StyleWithLuaFormatter(formatter LuaFormatterFunc) *Style
// Example using a predefined style output := dumper.DumpConfig(myConfig, dumper.IndentStyle)Add a custom directive to any block
masterTo add a completely new directive to an existing block, find the target block, cast it to a
*config.Block, and append a new*config.Directiveto itsDirectivesslice.blocks := conf.FindDirectives(blockName) block := blocks[0].GetBlock() realBlock := block.(*config.Block) newDirective := &config.Directive{ Name: directiveName, Parameters: []string{directiveValue}, } realBlock.Directives = append(realBlock.Directives, newDirective)Update an existing directive in place
masterTo modify an existing directive (e.g., changing a
proxy_passURL), find the directive usingFindDirectives(name), iterate through the results, and update theParametersslice directly.directives := c.FindDirectives("proxy_pass") for _, directive := range directives { if directive.GetParameters()[0] == "http://old-url.com/" { directive.GetParameters()[0] = "http://new-url.com/" } }