syver package - github.com/krameff/syver - Go Packages

syver

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 32 Imported by: 0

README

Syver - Quick and Easy server validation

Syver - by Krameff Solutions Ltd

Note: This is a fork of the original goss-org/goss project created and maintained by @aelsabbahy. All original work remains under the Apache 2.0 license and full credit goes to the original author for building such a solid foundation. This fork exists to enable newer features and fixes to be developed and released. We are deeply grateful for the effort and care that went into the original project and aim to continue it in the same spirit.

Syver has since diverged from upstream. The work here is driven by client requirements rather than by tracking goss-org/goss, so the two projects will continue to differ and Syver does not aim for release-for-release parity with upstream. Compatibility with the gossfile format is a separate matter and is maintained deliberately: existing gossfiles, environment variables and wrapper scripts keep working. See goss vs Syver for exactly what differs.

Documentation

Note: For testing containers see the dsyver wrapper. There are also wrapper scripts for Kubernetes (ksyver) and Docker Compose (dcsyver). The goss-named dgoss, kgoss and dcgoss still ship alongside them and work unchanged.

Note: For some Docker/Kubernetes healthcheck, health endpoint, and container ordering examples, see the Docker/Kubernetes simplified health checks blog post.

Introduction

What is Syver?

Syver is a YAML based serverspec alternative tool for validating a server's configuration. It eases the process of writing tests by allowing the user to generate tests from the current system state. Once the test suite is written they can be executed, waited-on, or served as a health endpoint.

Why use Syver?
  • Syver is EASY!
  • Syver is FAST! - small-medium test suites are near instantaneous
  • Syver is SMALL! - <20MB single self-contained binary
Coming from goss?

Syver is the renamed continuation of the krameff/goss fork. Your gossfiles do not need to change. gossfile:, goss.yaml, GOSS_* env vars, the dgoss/dcgoss/kgoss wrappers and the -g flag all keep working.

See goss vs Syver for the full side-by-side comparison, including the single intentional breaking change.

Installation

Note: For macOS and Windows, see platform support.

Build from source or install release binaries — see installation.

This installs syver, the dsyver container wrapper, and dgoss as a compatibility shim.

Manual installation

Download pre-built binaries and install wrappers as described in installation.

Build it yourself
make build

Alternatively, you can build it with goreleaser. To build a binary, use goreleaser build, and to only build for the same OS and architecture as the machine you're building on, include the --single-target flag. The --clean flag will clean up any existing builds, and --snapshot will allow you to build against something other than a tag.

Here's an example:

$ goreleaser build --clean --single-target --snapshot
  • skipping validate...
  • cleaning distribution directory
  • loading environment variables
  • getting and validating git state
  • ignoring errors because this is a snapshot error=git doesn't contain any tags - either add a tag or use --snapshot
  • using tags previous= current=v0.0.0
  • pipe skipped or partially skipped reason=disabled during snapshot mode
  • parsing tag
  • setting defaults
  • partial
  • snapshotting
  • building snapshot... version=0.0.1-next
  • running before hooks
  • running hook=go mod tidy
  • ensuring distribution directory
  • setting up metadata
  • writing release metadata
  • loading go mod information
  • build prerequisites
  • building binaries
  • partial build match=target=linux_arm64_v8.0
  • building paths=cmd/syver binaries=syver target=linux_arm64_v8.0
  • took: 31s
  • writing artifacts metadata
  • build succeeded after 31s
  • thanks for using GoReleaser!
$ tree dist
dist
├── artifacts.json
├── binaries_linux_arm64_v8.0
│   └── syver <- your binary
├── config.yaml
└── metadata.json

2 directories, 4 files

Full Documentation

Published at syver.readthedocs.io, built from docs/ in this repository. The site is built from main, so it reflects the latest release rather than unreleased work on devel.

Releases

Release history records every release and the commit it points at. CHANGELOG.md has the detail of what changed in each.

Using the container image

Using the Syver container image

Quick start

Writing a simple sshd test

An initial set of tests can be derived from the system state by using the add or autoadd commands.

Let's write a simple sshd test using autoadd.

# Running it as root will allow it to also detect ports
$ sudo syver autoadd sshd

On spec filenames: syver looks for syver.yaml, syver.yml, goss.yaml and goss.yml, in that order, and writes to whichever it found. When none exist it creates syver.yaml. The goss-named files keep working indefinitely, so an existing goss.yaml needs no migration; syver.yaml is simply the preferred name for new work. Note this is the filename only. Inside the file the import key stays gossfile: (syverfile: is accepted as an input alias).

Generated syver.yaml:

port:
  tcp:22:
    listening: true
    ip:
    - 0.0.0.0
    pid:
    - 1234
  tcp6:22:
    listening: true
    ip:
    - '::'
    pid:
    - 1234
service:
  sshd:
    enabled: true
    running: true
user:
  sshd:
    exists: true
    uid: 74
    gid: 74
    groups:
    - sshd
    home: /var/empty/sshd
    shell: /sbin/nologin
group:
  sshd:
    exists: true
    gid: 74
process:
  sshd:
    running: true
    status:
    - sleep
    user:
    - root

Now that we have a test suite, we can:

  • Run it once
$ syver validate
...............

Total Duration: 0.021s # <- yeah, it's that fast..
Count: 15, Failed: 0
  • Edit it to use templates, and run with a vars file
syver --vars vars.yaml validate
  • keep running it until the system enters a valid state or we timeout
syver validate --retry-timeout 30s --sleep 1s
  • serve the tests as a health endpoint
$ syver serve &
$ curl localhost:8080/healthz

# JSON endpoint
$ syver serve --format json &
$ curl localhost:8080/healthz

# rspecish response via content negotiation
$ syver serve --format json &
$ curl -H "Accept: application/vnd.goss-rspecish" localhost:8080/healthz
Manually editing Syver files

Syver files can be manually edited to improve readability and expressiveness of tests.

A Json draft 7 schema in docs/schema.yaml makes it easier to edit simple syver.yaml / goss.yaml files in IDEs, providing usual coding assistance such as inline documentation, completion and static analysis. See #793 for screenshots.

For example, to configure the Json schema in JetBrains intellij IDEA, follow documented instructions, with arguments such as:

  • schema url=docs/schema.yaml (path from the repository root)
  • schema version=Json schema version 7
  • file path pattern=*/syver.yaml (add a second pattern for */goss.yaml if you still have goss-named specs)

In addition, Syver files can also be further manually edited (without yet full json support) to use:

A typo'd top-level key (writing prot: instead of port:, for example) no longer validates silently: syver logs a [WARN] for it, with a suggestion when it can make one. See Unknown top-level keys.

Some examples:

user:
  sshd:
    title: UID must be between 50-100, GID doesn't matter. home is flexible
    meta:
      desc: Ensure sshd is enabled and running since it's needed for system management
      sev: 5
    exists: true
    uid:
      # Validate that UID is between 50 and 100
      and:
        gt: 50
        lt: 100
    home:
      # Home can be any of the following
      or:
      - /var/empty/sshd
      - /var/run/sshd

package:
  kernel:
    installed: true
    versions:
      # Must have 3 kernels and none of them can be 4.4.0
      and:
      - have-len: 3
      - not:
          contain-element: 4.4.0

  # Loaded from --vars YAML/JSON file
  {{.Vars.package}}:
    installed: true

{{if eq .Env.OS "centos"}}
  # This test is only when $OS environment variable is set to "centos"
  libselinux:
    installed: true
{{end}}

Spec files with templates can still be validated through the Json schema after being rendered using the syver render command. See example below

$ cd docs
$ syver --vars ./vars.yaml render > rendered_syver.yaml
# proceed with json schema validation of rendered_syver.yaml in your favorite IDE
# or in one of the Json schema validator listed in https://json-schema.org/implementations.html
# The following example is for a Linux AMD64 host
$ curl -LO https://github.com/neilpa/yajsv/releases/download/v1.4.1/yajsv.linux.amd64
$ chmod a+x yajsv.linux.amd64
$ sudo mv yajsv.linux.amd64 /usr/sbin/yajsv

$ yajsv -s schema.yaml rendered_syver.yaml
rendered_syver.yaml: pass

Full list of available Json schema validators can be found in https://json-schema.org/implementations.html#validator-command%20line

Discovery and test dependencies

Run lightweight discovery checks before the main suite and use the results in templates, or declare depends-on to skip dependents when a prerequisite fails. See Discovery and Test dependencies.

# Pre-run discovery, then validate main gossfile (preferred)
syver validate -g goss.yml --discover discovery.yaml

# Or inline discovery: in the same gossfile
syver validate -g goss-inline.yml

# Export discovery results for external tooling (unchanged)
syver validate -g discovery.yaml --format discovery

These examples pass goss-named files because that is what the checked-in fixtures are called. goss.yaml and goss.yml are still accepted, and always will be. -g takes any path, and a spec found by name is resolved across all four of syver.yaml, syver.yml, goss.yaml, goss.yml.

Fixtures: integration-tests/syver/examples/discovery/

Supported resources

  • package - add new package
  • file - add new file
  • addr - add new remote address:port - ex: google.com:80
  • port - add new listening [protocol]:port - ex: 80 or udp:123
  • service - add new service
  • user - add new user
  • group - add new group
  • command - add new command
  • dns - add new dns
  • process - add new process name
  • registry - add new Windows registry key or value (Windows only)
  • kernel-param - add new kernel-param
  • mount - add new mount
  • interface - add new network interface
  • http - add new network http url with proxy support
  • syver (alias: goss) - add new syver file, it will be imported from this one
  • matching - test for matches in supplied content

Supported output formats

  • rspecish - (default) Similar to rspec output
  • documentation - Verbose test results
  • json - JSON, detailed test result
  • structured - JSON like json, plus a summary object and a human-readable summary-line on every result
  • tap - TAP style
  • junit - JUnit style
  • nagios - Nagios/Sensu compatible output /w exit code 2 for failures.
  • prometheus - Prometheus compatible output.
  • silent - No output. Avoids exposing system information (e.g. when serving tests as a healthcheck endpoint).

Community Contributions

These are third-party integrations written for upstream goss. They are listed because they still work, but each one invokes a binary named goss, and install.sh installs syver, dsyver and dgoss, but no goss. To use any of them, put a goss on your PATH pointing at syver:

sudo ln -s "$(command -v syver)" /usr/local/bin/goss

The gossfiles these tools generate and consume need no changes; only the binary name differs. None of them are maintained by this project.

  • goss-ansible - Ansible module for Goss.
  • degoss - Ansible role for installing, running, and removing Goss in a single go.
  • ansible-goss-install - Ansible role for installing Goss (option for install as user or root)
  • kitchen-goss - A test-kitchen verifier plugin for Goss.
  • goss-fpm-files - Might be useful for building goss system packages.
  • packer-provisioner-goss - A packer plugin to run Goss as a provision step.
  • gossboss - Collect and view aggregated Goss test results from multiple remote Goss servers.

Limitations

syver works well on Linux, but support on Windows & macOS is alpha. See platform support.

The following tests have limitations.

Package:

  • rpm
  • deb
  • Alpine apk
  • pacman

Service:

  • systemd
  • sysV init
  • OpenRC init
  • Upstart

Port:

  • Port state is read from /proc/net/{tcp,udp,tcp6,udp6} on Linux, where it is fully supported. It is not implemented on macOS or Windows: measured on Windows Server 2025, every assertion returns "not implemented yet". See platform support, which is the authoritative per-resource matrix.
  • On Linux, if one of those files exists but contains a line syver can't parse (an unexpected IP/port/uid encoding, typically from a non-standard procfs, e.g. inside certain containers or network namespaces), the affected port resource now fails with an explicit Error: block in the output instead of silently reporting the port as not listening. To investigate: note which protocol failed from the resource id (tcp, tcp6, udp, udp6), then inspect the corresponding file directly, e.g. cat /proc/net/tcp6, and compare its columns against a working host. A missing or unreadable file is not an error case -- it's treated as "no ports" for that protocol.

Documentation

Index

Constants

View Source
const (
	UNSET = iota
	JSON
	YAML
)

Variables

This section is empty.

Functions

func AddResource

func AddResource(fileName string, syverConfig SyverConfig, resourceName, key string, config util.Config, sys *system.System) error

AddResource adds a single resource to fileName.

FEAT-007 G1: this used to be a 16-case switch on resourceName, each case hand-calling syverConfig.<Field>.AppendSysResource(...) directly. That's replaced with a descriptor lookup by Name (desc.AppendSys, which closes over the type-specific system+resource construction -- see resource/descriptor.go) plus resource.UpsertLive, which needs to know *which* SyverConfig field to insert into without resource-package code ever importing SyverConfig (the two packages can't import each other). configAccessors[desc.Key].Field supplies that: a pointer to the live struct field, e.g. &syverConfig.Ports -- not a copy, so the new resource actually lands in the file WriteJSON writes below. Using the Get member here instead (a snapshot copy) was the exact trap this spec calls out: `syver add` would silently stop writing new resources.

func AddResources

func AddResources(fileName, resourceName string, keys []string, c *util.Config) error

AddResources is a simple wrapper to add multiple resources

func AutoAddResource

func AutoAddResource(fileName string, syverConfig SyverConfig, key string, c *util.Config, sys *system.System) error

AutoAddResource adds a single resource to fileName with automatic detection of the type of resource

func AutoAddResources

func AutoAddResources(fileName string, keys []string, c *util.Config) error

AutoAddResources is a simple wrapper to add multiple resources

func NewTemplateFilter

func NewTemplateFilter(varsFiles []string, varsInline string, discovered map[string]bool) (func([]byte) ([]byte, error), error)

NewTemplateFilter creates a new Template Filter based in the file and inline variables.

func RenderJSON

func RenderJSON(c *util.Config) (string, error)

RenderJSON reads json file recursively returning string

func Serve

func Serve(ctx context.Context, c *util.Config) error

func Validate

func Validate(ctx context.Context, c *util.Config) (code int, err error)

Validate performs validation, writes formatted output to stdout by default and supports retries and more, this is the full featured Validate used by the CLI invocation and will produce output to StdOut. Use ValidateResults for programmatic access

func ValidateConfig

func ValidateConfig(ctx context.Context, c *util.Config, syverConfig *SyverConfig) (code int, err error)

func ValidateResults

func ValidateResults(ctx context.Context, c *util.Config) (results <-chan []resource.TestResult, err error)

ValidateResults performs validation and provides programmatic access to validation results no retries or outputs are supported

func ValidateVarsInline added in v0.11.0

func ValidateVarsInline(s string) error

ValidateVarsInline reports whether s is usable as the value of --vars-inline, without keeping the parsed result.

It exists so the CLI can reject a bad value AT FLAG-PARSE TIME rather than several steps later while loading vars. The difference is not cosmetic. A shell that mangles the argument -- cmd.exe does, because it does not treat `'` as a quote character -- leaves syver holding a fragment plus a stray argument, and the stray one is then read as a subcommand, so the user gets a "No help topic" error naming that fragment, pointing nowhere near the flag. Validating here makes the message name --vars-inline and quote the value it actually received, which is what tells someone their shell split the command line.

func WriteJSON

func WriteJSON(filePath string, syverConfig SyverConfig) error

Types

type DiscoveryConfig

type DiscoveryConfig struct {
	Files        resource.FileMap        `json:"file,omitempty" yaml:"file,omitempty"`
	Packages     resource.PackageMap     `json:"package,omitempty" yaml:"package,omitempty"`
	Addrs        resource.AddrMap        `json:"addr,omitempty" yaml:"addr,omitempty"`
	Ports        resource.PortMap        `json:"port,omitempty" yaml:"port,omitempty"`
	Services     resource.ServiceMap     `json:"service,omitempty" yaml:"service,omitempty"`
	Users        resource.UserMap        `json:"user,omitempty" yaml:"user,omitempty"`
	Groups       resource.GroupMap       `json:"group,omitempty" yaml:"group,omitempty"`
	Commands     resource.CommandMap     `json:"command,omitempty" yaml:"command,omitempty"`
	DNS          resource.DNSMap         `json:"dns,omitempty" yaml:"dns,omitempty"`
	Processes    resource.ProcessMap     `json:"process,omitempty" yaml:"process,omitempty"`
	KernelParams resource.KernelParamMap `json:"kernel-param,omitempty" yaml:"kernel-param,omitempty"`
	Mounts       resource.MountMap       `json:"mount,omitempty" yaml:"mount,omitempty"`
	Interfaces   resource.InterfaceMap   `json:"interface,omitempty" yaml:"interface,omitempty"`
	HTTPs        resource.HTTPMap        `json:"http,omitempty" yaml:"http,omitempty"`
	Matchings    resource.MatchingMap    `json:"matching,omitempty" yaml:"matching,omitempty"`
	Registries   resource.RegistryMap    `json:"registry,omitempty" yaml:"registry,omitempty"`
}

DiscoveryConfig holds discovery-phase tests keyed by resource type.

func (*DiscoveryConfig) Entries

func (c *DiscoveryConfig) Entries() ([]DiscoveryEntry, error)

func (*DiscoveryConfig) IsEmpty

func (c *DiscoveryConfig) IsEmpty() bool

func (*DiscoveryConfig) Merge

func (c *DiscoveryConfig) Merge(g2 DiscoveryConfig)

type DiscoveryEntry

type DiscoveryEntry struct {
	Register string
	Resource resource.Resource
}

DiscoveryEntry pairs a register name with a discovery resource.

type SyverConfig

type SyverConfig struct {
	Discovery  DiscoveryConfig       `json:"discovery,omitempty" yaml:"discovery,omitempty"`
	Files      resource.FileMap      `json:"file,omitempty" yaml:"file,omitempty"`
	Packages   resource.PackageMap   `json:"package,omitempty" yaml:"package,omitempty"`
	Addrs      resource.AddrMap      `json:"addr,omitempty" yaml:"addr,omitempty"`
	Ports      resource.PortMap      `json:"port,omitempty" yaml:"port,omitempty"`
	Services   resource.ServiceMap   `json:"service,omitempty" yaml:"service,omitempty"`
	Users      resource.UserMap      `json:"user,omitempty" yaml:"user,omitempty"`
	Groups     resource.GroupMap     `json:"group,omitempty" yaml:"group,omitempty"`
	Commands   resource.CommandMap   `json:"command,omitempty" yaml:"command,omitempty"`
	DNS        resource.DNSMap       `json:"dns,omitempty" yaml:"dns,omitempty"`
	Processes  resource.ProcessMap   `json:"process,omitempty" yaml:"process,omitempty"`
	Syverfiles resource.SyverfileMap `json:"gossfile,omitempty" yaml:"gossfile,omitempty"`
	// SyverfileAlias is an input-only alias for Syverfiles: a gossfile
	// written with `syverfile:` entries decodes here, gets folded into
	// Syverfiles (see ReadJSONData in store.go), and is then nil'd out --
	// it is never itself written back out (omitempty drops it once nil).
	//
	// NOTE (FEAT-007): this field intentionally has no dispatch.go
	// accessor entry and is never touched by the configAccessors loops
	// below -- it isn't a registered resource type, it's pure store.go
	// decode plumbing (see store.go:255-268). Handled explicitly here,
	// same as before the refactor.
	SyverfileAlias resource.SyverfileMap   `json:"syverfile,omitempty" yaml:"syverfile,omitempty"`
	KernelParams   resource.KernelParamMap `json:"kernel-param,omitempty" yaml:"kernel-param,omitempty"`
	Mounts         resource.MountMap       `json:"mount,omitempty" yaml:"mount,omitempty"`
	Interfaces     resource.InterfaceMap   `json:"interface,omitempty" yaml:"interface,omitempty"`
	HTTPs          resource.HTTPMap        `json:"http,omitempty" yaml:"http,omitempty"`
	Matchings      resource.MatchingMap    `json:"matching,omitempty" yaml:"matching,omitempty"`
	Registries     resource.RegistryMap    `json:"registry,omitempty" yaml:"registry,omitempty"`
}

func NewSyverConfig

func NewSyverConfig() *SyverConfig

NewSyverConfig builds an empty SyverConfig with every map field initialised (FEAT-007: replaces two 16-entry make() blocks with a loop over configAccessors/discoveryAccessors). gossfile has no accessor Make (see the accessor table's comment in dispatch.go) so Syverfiles and SyverfileAlias are still made explicitly, exactly as before.

func ReadJSON

func ReadJSON(filePath string) (SyverConfig, error)

ReadJSON Reads json file returning SyverConfig

func ReadJSONData

func ReadJSONData(data []byte, detectFormat bool, path string) (SyverConfig, error)

ReadJSONData Reads json byte array returning SyverConfig. path names the spec this data came from and is used only for the top-level key guard's warnings below (D7 in PLAN_toplevel_key_guard.md) -- pass "" if there is none (e.g. content assembled in memory rather than read from a file).

func (*SyverConfig) Merge

func (c *SyverConfig) Merge(g2 SyverConfig)

Merge consumes all the resources in g2 into c, duplicate resources will be overwritten with the ones in g2

func (*SyverConfig) Resources

func (c *SyverConfig) Resources() []resource.Resource

Resources returns every validation-eligible resource (FEAT-007: replaces the 16-argument genericConcatMaps call with a loop over resourceOrder; see the accessor table's InValidation field, which is what "eligible" means here -- gossfile is the one registered type excluded).

type TemplateFilter

type TemplateFilter func([]byte) ([]byte, error)

TemplateFilter is the type of the Goss Template Filter which include custom variables and functions.

func NewPeekTemplateFilter

func NewPeekTemplateFilter(varsFiles []string, varsInline string) (TemplateFilter, error)

NewPeekTemplateFilter renders templates before discovery results exist. Missing .Discovered keys evaluate as false instead of failing template execution.

type TmplVars

type TmplVars struct {
	Vars       map[string]any
	Discovered map[string]any
}

func (*TmplVars) Env

func (t *TmplVars) Env() map[string]string

Directories

Path Synopsis
cmd
syver command

Jump to

Keyboard shortcuts

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