ir

package
v0.1.2 Latest Latest
Warning

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

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

Documentation

Overview

Package ir holds the normalised model that rules operate on.

Both compose files and native Quadlet units parse into this model, so a rule never learns which produced it. That is what lets "audit what you converted" and "audit what you hand-wrote" be the same code path.

The subject of a rule is a Project, not a Unit. Several rules cannot be answered from one unit alone: whether a bind source is shared between units (QD001 and QD002), whether sibling containers can resolve each other (QD030), whether a name collides (QD032). Making Project the subject from the start avoids retrofitting it later. See docs/spec-review.md finding F3.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type EnvVar

type EnvVar struct {
	Name  string
	Value string
	Line  int
}

EnvVar is one environment assignment.

type KeyValue

type KeyValue struct {
	Key   string
	Value string
	Line  int
}

KeyValue is a key and its value with the line it appeared on.

type Mount

type Mount struct {
	// Source is the host path or volume name, empty for anonymous mounts.
	Source string
	// Destination is the path inside the container.
	Destination string
	// Options are the comma-separated options after the second colon,
	// e.g. `ro`, `Z`, `z`, `U`.
	Options []string
	Type    MountType
	// UnitRef is the referenced Quadlet unit for a `foo.volume` source,
	// otherwise empty.
	UnitRef string
	// Line is where this mount was declared, for citing in findings.
	Line int
	// Raw is the original value, so a fix can rewrite it precisely.
	Raw string
}

Mount is one `Volume=` entry, decomposed.

func ParseMount

func ParseMount(value string, line int) Mount

ParseMount decomposes one `Volume=` value.

The grammar is `[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]` (podman-systemd.unit(5)). A source that ends in `.volume` refers to a sibling Quadlet unit, which Podman materialises as a volume named `systemd-$name`.

func (Mount) HasOption

func (m Mount) HasOption(opt string) bool

HasOption reports whether an option is present, case-sensitively. Case matters here: `:z` and `:Z` mean different things, shared versus private relabelling, and conflating them is the bug QD002 exists to catch.

func (Mount) HasSELinuxLabel

func (m Mount) HasSELinuxLabel() bool

HasSELinuxLabel reports whether either relabelling option is present.

func (Mount) VolumeObjectName

func (m Mount) VolumeObjectName() string

VolumeObjectName returns the Podman volume name a named-volume source resolves to. Quadlet prefixes volumes it creates with `systemd-`, so `pg.volume` becomes `systemd-pg`. Verified against Podman 5.8.4.

type MountType

type MountType string

MountType distinguishes a host path from a Podman-managed named volume, because the rules that apply differ sharply between them.

const (
	// MountBind is a host directory or file mounted into the container.
	// SELinux relabelling rules apply.
	MountBind MountType = "bind"
	// MountNamed is a Podman named volume, including the `foo.volume` form
	// that refers to a sibling Quadlet unit.
	MountNamed MountType = "named"
	// MountAnonymous is a mount with no source, e.g. `Volume=/data`.
	MountAnonymous MountType = "anonymous"
)

type Port

type Port struct {
	HostIP        string
	HostPort      int
	ContainerPort int
	Protocol      string
	Line          int
	Raw           string
}

Port is one `PublishPort=` entry, decomposed.

func ParsePort

func ParsePort(value string, line int) (Port, bool)

ParsePort decomposes one `PublishPort=` value.

The grammar is `[[ip:][hostPort]:]containerPort[/protocol]`, so the host port is the second-to-last colon-separated field when there is more than one.

type Project

type Project struct {
	// Root is the directory the units came from, for reporting.
	Root string
	// Units are the project's units in a deterministic order.
	Units []*Unit
}

Project is a set of units considered together: the subject of every rule.

func LoadProject

func LoadProject(root string) (*Project, error)

LoadProject reads every Quadlet unit under root into a project.

Files whose extension is not a Quadlet unit type are ignored, so pointing quaddoc at a directory containing a README or a compose file is harmless.

func (*Project) BindSourceUsage

func (p *Project) BindSourceUsage() map[string]int

BindSourceUsage counts, for every bind-mount source in the project, how many distinct units mount it.

This is computed once and shared, which is what keeps QD001 and QD002 from contradicting each other: QD001 fires only when no relabelling option is present, QD002 only when `:Z` is used on a source this map shows is shared. Deriving sharing independently in each rule would let a fix from one produce a finding from the other. See docs/spec-review.md finding F3.

func (*Project) Containers

func (p *Project) Containers() []*Unit

Containers returns just the container units.

func (*Project) NamedVolumeUsers

func (p *Project) NamedVolumeUsers() map[string][]*Unit

NamedVolumeUsers returns, for each named volume, the units that mount it. QD012 needs this to tell whether a volume a non-root container mounts was previously initialised by a root one.

func (*Project) Sort

func (p *Project) Sort()

Sort orders units deterministically, so output does not depend on the order the filesystem happened to return.

func (*Project) UnitByName

func (p *Project) UnitByName(name string, kind UnitKind) (*Unit, bool)

UnitByName finds a unit by name and kind, e.g. ("web", KindVolume).

type SourceEntry

type SourceEntry struct {
	Section string
	Key     string
	Value   string
	Line    int
}

SourceEntry is one assignment as it appeared in the file.

type SourceFile

type SourceFile interface {
	// Values returns every value of a key within a section, in file order.
	Values(section, key string) []string
	// Render writes the file back out.
	Render() string
}

SourceFile is the parsed-file behaviour the IR needs, kept as an interface so package ir does not depend on the parser and generated units can omit it.

type Unit

type Unit struct {
	// Path is the file this unit came from, or a synthetic name when the
	// unit was generated by conversion rather than read from disk.
	Path string
	// Name is the file's base name without its extension: `web.container`
	// has the name `web`.
	Name string
	Kind UnitKind

	// Image is the `Image=` value for a container unit.
	Image string
	// Mounts are the decomposed `Volume=` entries.
	Mounts []Mount
	// Ports are the decomposed `PublishPort=` entries.
	Ports []Port
	// Networks are the raw `Network=` values.
	Networks []string
	// Environment holds `Environment=` entries in declaration order.
	Environment []EnvVar
	// User and Group are the `User=` and `Group=` values.
	User  string
	Group string
	// GroupAdd holds the `GroupAdd=` values.
	GroupAdd []string
	// UserNS is the `UserNS=` value.
	UserNS string
	// AutoUpdate is the `AutoUpdate=` value.
	AutoUpdate string
	// Pod is the `Pod=` value, naming the pod unit this container joins.
	Pod string
	// Notify is the `Notify=` value.
	Notify string
	// Restart is `Restart=` from the `[Service]` section.
	Restart string
	// HasInstall records whether an `[Install]` section is present at all,
	// even empty. QD022 turns on that distinction.
	HasInstall bool
	// InstallKeys are the keys found in `[Install]`.
	InstallKeys []KeyValue
	// HasHealthCmd records whether a healthcheck is configured, which
	// `Notify=healthy` requires.
	HasHealthCmd bool

	// Source is the parsed file, retained so fixes can rewrite it and rules
	// can reach keys the IR does not model. Nil for generated units.
	Source SourceFile

	// Entries is every assignment in the file, with its section and line, so
	// rules can examine keys the IR does not model. QD042 needs this to spot
	// a key that is not modelled *because* it is not a real key.
	Entries []SourceEntry
	// contains filtered or unexported fields
}

Unit is one Quadlet unit file in normalised form.

func FromParsed

func FromParsed(f *quadlet.File) *Unit

FromParsed normalises an already-parsed unit file.

func LoadUnit

func LoadUnit(path string) (*Unit, error)

LoadUnit reads and normalises one Quadlet unit file.

func (*Unit) KeyLine

func (u *Unit) KeyLine(key string) int

KeyLine returns the line a scalar key was declared on, or 0 if the unit did not set it. Findings are more useful pointing at a line than at a whole file.

func (*Unit) SetKeyLine

func (u *Unit) SetKeyLine(key string, line int)

SetKeyLine records where a key was declared. Used by loaders.

type UnitKind

type UnitKind string

UnitKind is the type of a Quadlet unit, determined by its file extension.

const (
	KindContainer UnitKind = "container"
	KindVolume    UnitKind = "volume"
	KindNetwork   UnitKind = "network"
	KindPod       UnitKind = "pod"
	KindUnknown   UnitKind = "unknown"
)

func KindFromPath

func KindFromPath(path string) UnitKind

KindFromPath derives the unit kind from a file name's extension.

func (UnitKind) Section

func (k UnitKind) Section() string

Section returns the Quadlet section name a unit kind uses, e.g. `[Container]`.

Jump to

Keyboard shortcuts

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