confetti

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

confetti

confetti is a schema-aware, offline engine for network-device CLI configurations: parse, validate (Juniper-style commit check), canonicalize, remediate, roll back, diff, and merge. It processes text without device connections. The core contains no vendor-specific logic.

  • Offline and deterministic. Everything runs on text you already have; the same inputs always produce the same artifact.
  • Grammar as data. A platform is a schema.Schema you author in Go: line templates with typed captures, sections, cross-references, negation and reset forms, raw blocks (banners), toggle pairs, list-valued args, dual-form spellings, protected nodes.
  • Safety constraints. Remediation artifacts are dependency-ordered (definitions before referrers on add, referrers first on remove), protected nodes refuse deletion in every policy, and on any Error no artifact is returned.

Install

go get github.com/acidsailor/confetti

Usage

Build an Engine from a schema.Schema describing your platform's grammar, then drive it with text:

e := confetti.New(mySchema, confetti.WithPolicy(diag.Policy{Strict: true}))

// Parse and check referential integrity.
cfg, diags := e.Import(runningText)
diags = e.CommitCheck(cfg)

// Render the canonical form.
out, diags := e.Render(cfg)

// Generate CLI commands that change running into intended.
res, diags := e.Remediate(running, intended)
artifact, _ := e.Render(res.Tree)

// Generate the inverse artifact with the same argument order.
inv, diags := e.Rollback(running, intended)

// Generate a git-diff-style view without a commit check.
view, diags := e.Compare(running, intended)

// Merge fragments. Strict mode rejects conflicts; lenient mode uses the last value.
merged, diags := e.Merge(base, overlay)
diags = e.CommitCheck(merged)

A schema is declared as data:

s := schema.New()
vlan := s.Node("vlan {{ id:vlan }}").Card(schema.ZeroToN).Kind("vlan").Key("id")
vlan.Child("name {{ text:word }}").Card(schema.ZeroToOne).MarkIdempotent()
iface := s.Node("interface {{ name:ifname }}").Card(schema.ZeroToN)
iface.Child("switchport access vlan {{ vlan:vlan }}").
	Card(schema.ZeroToOne).MarkIdempotent().Ref("vlan", "vlan.id")

A lenient diag.Policy downgrades unknown commands to warnings for existing configurations.

See example_test.go for a runnable schema. The schemas in internal/fixture/ cover additional features but model imaginary platforms and are not suitable for real devices. Define production platform schemas in downstream repositories.

Documentation

  • docs/design.md describes the architecture, pipelines, and design invariants.
  • docs/fixtures.md records fixture grammar lineage and unverified assumptions.
  • Package-level doc.go files cover each package's role and contracts.

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package confetti is a schema-aware, offline engine for network-device CLI configurations. It parses, validates, canonicalizes, remediates, rolls back, compares, and merges configuration text without connecting to a device.

The Engine ties a platform grammar (schema.Schema), a strict/lenient policy (diag.Policy), and the import/export transform pipelines. Grammar is data: platforms are authored as schemas and live outside this module (the in-repo internal/fixture schemas are test fixtures and authoring references). See docs/design.md for the architecture and the design invariants and docs/fixtures.md for fixture grammar lineage.

Remediate and Rollback run a commit check on their goal. Render, Merge, and Compare do not because callers can use them with incomplete configurations.

Example

Example imports two configurations and renders the remediation commands.

package main

import (
	"fmt"

	confetti "github.com/acidsailor/confetti"
	"github.com/acidsailor/confetti/schema"
)

// switchSchema defines VLANs and access ports that reference them.
func switchSchema() *schema.Schema {
	s := schema.New()

	vlan := s.Node("vlan {{ id:uint }}").
		Card(schema.ZeroToN).
		Kind("vlan").
		Key("id")
	vlan.Child("name {{ text:word }}").Card(schema.ZeroToOne).MarkIdempotent()

	iface := s.Node("interface {{ name:word }}").
		Card(schema.ZeroToN).
		Kind("interface").
		Key("name")
	iface.Child("switchport access vlan {{ vlan:uint }}").
		Card(schema.ZeroToOne).MarkIdempotent().Ref("vlan", "vlan.id")

	return s
}

func main() {
	e := confetti.New(switchSchema())

	running, _ := e.Import(
		"vlan 10\n" +
			"  name USERS\n" +
			"vlan 30\n" +
			"  name LEGACY\n" +
			"interface Ethernet1/1\n" +
			"  switchport access vlan 10\n")

	intended, _ := e.Import(
		"vlan 10\n" +
			"  name STAFF\n" +
			"vlan 20\n" +
			"  name GUESTS\n" +
			"interface Ethernet1/1\n" +
			"  switchport access vlan 20\n")

	res, d := e.Remediate(running, intended)
	if d.HasErrors() {
		fmt.Print(d.String())
		return
	}

	artifact, _ := e.Render(res.Tree)
	fmt.Print(artifact)
}
Output:
vlan 10
  name STAFF
vlan 20
  name GUESTS
interface Ethernet1/1
  switchport access vlan 20
no vlan 30

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine ties a schema, a policy, and the import/export transform pipelines.

func New

func New(s *schema.Schema, opts ...Option) *Engine

New constructs an Engine for the given schema.

func (*Engine) CommitCheck

func (e *Engine) CommitCheck(cfg *tree.Config) *diag.Diagnostics

CommitCheck runs Phase B referential integrity over an assembled tree.

Example

ExampleEngine_CommitCheck reports a reference to an undefined VLAN.

package main

import (
	"fmt"

	confetti "github.com/acidsailor/confetti"
	"github.com/acidsailor/confetti/schema"
)

// switchSchema defines VLANs and access ports that reference them.
func switchSchema() *schema.Schema {
	s := schema.New()

	vlan := s.Node("vlan {{ id:uint }}").
		Card(schema.ZeroToN).
		Kind("vlan").
		Key("id")
	vlan.Child("name {{ text:word }}").Card(schema.ZeroToOne).MarkIdempotent()

	iface := s.Node("interface {{ name:word }}").
		Card(schema.ZeroToN).
		Kind("interface").
		Key("name")
	iface.Child("switchport access vlan {{ vlan:uint }}").
		Card(schema.ZeroToOne).MarkIdempotent().Ref("vlan", "vlan.id")

	return s
}

func main() {
	e := confetti.New(switchSchema())

	cfg, _ := e.Import(
		"interface Ethernet1/1\n" +
			"  switchport access vlan 999\n")

	d := e.CommitCheck(cfg)
	fmt.Print(d.String())
}
Output:
2: error: interface Ethernet1/1 / switchport access vlan 999: vlan "999" does not exist

func (*Engine) Compare

func (e *Engine) Compare(
	running, intended *tree.Config,
) (string, *diag.Diagnostics)

Compare returns a git-diff-style view without commit-checking either input.

func (*Engine) Import

func (e *Engine) Import(text string) (*tree.Config, *diag.Diagnostics)

Import applies text rules outside raw blocks, parses and folds the input, applies tree transforms, and runs Phase A validation.

func (*Engine) Merge

func (e *Engine) Merge(
	parts ...*tree.Config,
) (*tree.Config, *diag.Diagnostics)

Merge combines fragments in order without running a commit check; strict conflicts keep the first value, lenient conflicts keep the last, and list slots form a union.

func (*Engine) Remediate

func (e *Engine) Remediate(
	running, intended *tree.Config,
) (*remediate.Result, *diag.Diagnostics)

Remediate checks intended and returns the operation-tagged difference from running to intended.

func (*Engine) Render

func (e *Engine) Render(cfg *tree.Config) (string, *diag.Diagnostics)

Render applies tree transforms, renders canonical text, and applies text rules outside raw blocks.

func (*Engine) Rollback

func (e *Engine) Rollback(
	running, intended *tree.Config,
) (*remediate.Result, *diag.Diagnostics)

Rollback checks running and returns the inverse of Remediate with the same argument order; restoration uses canonical parsed content, not original bytes.

type Option

type Option func(*Engine)

Option configures an Engine.

func WithExportText

func WithExportText(rules ...transform.TextRule) Option

WithExportText appends export-side (post-render) text transforms.

func WithExportTree

func WithExportTree(ts ...transform.TreeTransform) Option

WithExportTree appends export-side (pre-render) tree transforms.

func WithImportText

func WithImportText(rules ...transform.TextRule) Option

WithImportText appends import-side (pre-parse) text transforms.

func WithImportTree

func WithImportTree(ts ...transform.TreeTransform) Option

WithImportTree appends import-side (post-parse) tree transforms.

func WithPolicy

func WithPolicy(p diag.Policy) Option

WithPolicy sets the strict/lenient policy.

Directories

Path Synopsis
Package compare renders a remediation change log in a git-diff-style format.
Package compare renders a remediation change log in a git-diff-style format.
Package diag defines Error and Warning diagnostics with optional 1-based source lines.
Package diag defines Error and Warning diagnostics with optional 1-based source lines.
Package graph defines the remediation-ordering dependency graph passed to schema OrderHooks.
Package graph defines the remediation-ordering dependency graph passed to schema OrderHooks.
internal
fixture/alpha
Package alpha defines an imaginary switch CLI fixture for blocks, toggles, physical-port resets, feature gates, list values, and dual-form membership.
Package alpha defines an imaginary switch CLI fixture for blocks, toggles, physical-port resets, feature gates, list values, and dual-form membership.
fixture/beta
Package beta defines an imaginary switch CLI fixture for composite keys, EmptyOnRemove, and self-union.
Package beta defines an imaginary switch CLI fixture for composite keys, EmptyOnRemove, and self-union.
ident
Package ident defines the node-pairing identity shared by parse, remediate, merge, and validate.
Package ident defines the node-pairing identity shared by parse, remediate, merge, and validate.
lcp
Package lcp computes the longest-common-prefix length of ordered paths.
Package lcp computes the longest-common-prefix length of ordered paths.
listval
Package listval defines the separator and range grammar for list-valued arguments.
Package listval defines the separator and range grammar for list-valued arguments.
testtypes
Package testtypes defines the ifname, ipv4, vlan, and asn value types used by core package tests.
Package testtypes defines the ifname, ipv4, vlan, and asn value types used by core package tests.
valcheck
Package valcheck holds the value.Type Check helpers shared by the fixture schemas and the core test types.
Package valcheck holds the value.Type Check helpers shared by the fixture schemas and the core test types.
Package merge assembles configuration fragments from left to right in a new tree.
Package merge assembles configuration fragments from left to right in a new tree.
Package parse converts configuration text to a tree.Config.
Package parse converts configuration text to a tree.Config.
Package remediate computes the ordered CLI commands that change running configuration into intended configuration.
Package remediate computes the ordered CLI commands that change running configuration into intended configuration.
Package render converts a tree.Config to canonical text.
Package render converts a tree.Config to canonical text.
Package schema declares a platform grammar as a tree of node definitions, each a line template with typed captures ("vlan {{ id:vlan }}"), plus the metadata used by other packages: cardinality, Kind/Key identity, cross-references (Ref), prerequisite Kinds (Requires), negation and reset forms (NegateAs/NegateDefault/NegateFunc), raw-block capture (BlockDelim/BlockUntil), toggle groups (Toggles), list-valued args (List/ListDelta/ListKeywords/ListContinues), dual-form spellings (Members/RespellAs), and Protected deletion constraints.
Package schema declares a platform grammar as a tree of node definitions, each a line template with typed captures ("vlan {{ id:vlan }}"), plus the metadata used by other packages: cardinality, Kind/Key identity, cross-references (Ref), prerequisite Kinds (Requires), negation and reset forms (NegateAs/NegateDefault/NegateFunc), raw-block capture (BlockDelim/BlockUntil), toggle groups (Toggles), list-valued args (List/ListDelta/ListKeywords/ListContinues), dual-form spellings (Members/RespellAs), and Protected deletion constraints.
Package transform defines text rules and tree transforms.
Package transform defines text rules and tree transforms.
Package tree defines the configuration tree shared by all pipeline stages.
Package tree defines the configuration tree shared by all pipeline stages.
Package validate implements two phases of semantic validation.
Package validate implements two phases of semantic validation.
Package value defines the registry for typed captures such as "{{ id:vlan }}".
Package value defines the registry for typed captures such as "{{ id:vlan }}".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL