base

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2026 License: Apache-2.0 Imports: 66 Imported by: 0

Documentation

Index

Constants

View Source
const TestControllerName = "gate.haproxy.org/hug"
View Source
const (
	TestMapThroughRuntime = true
)

Variables

View Source
var RuntimeMaps = []string{
	"domain_wildcard_sni.map",
	"tcp_route.map",
	"listener_exact_match.map",
	"listener_route_exact_match.map",
	"listener_route_wildcard_match.map",
	"listener_wildcard_match.map",
	"path_exact.map",
	"path_prefix.map",
	"path_regex.map",
	"sni.map",
}

RuntimeMaps is the subset of StandardMaps that HAProxy loads via map() directives and are therefore accessible through the runtime socket. The three path maps are pre-loaded by no-op ACLs in haproxy.cfg so lua.find_route can reach them via core.get_patref.

View Source
var StandardMaps = []string{
	"domain_wildcard_sni.map",
	"listener_exact_match.map",
	"listener_route_exact_match.map",
	"listener_route_wildcard_match.map",
	"listener_wildcard_match.map",
	"path_exact.map",
	"path_prefix.map",
	"path_regex.map",
	"sni.map",
	"tcp_route.map",
}

Functions

func WriteKubeconfig

func WriteKubeconfig(cfg *rest.Config, rootPath string) (string, error)

WriteKubeconfig writes the given rest.Config to a kubeconfig file. It returns the path to the file and an error if it fails.

Types

type BaseSuite

type BaseSuite struct {
	suite.Suite
	// contains filtered or unexported fields
}

func (*BaseSuite) BackendFromManifest

func (b *BaseSuite) BackendFromManifest(manifestPath, manifestName string) *models.Backend

func (*BaseSuite) CheckEntryInMapFile added in v0.8.0

func (b *BaseSuite) CheckEntryInMapFile(mapFileRelativePath, key, value string) bool

func (*BaseSuite) CleanupFixtures

func (b *BaseSuite) CleanupFixtures(fixturePath string, manifestNames []string)

func (*BaseSuite) CleanupFixturesCheckMapFiles added in v0.9.0

func (b *BaseSuite) CleanupFixturesCheckMapFiles(fixturePath string, manifestNames []string, mapFileRelativePaths []string)

CleanupFixturesCheckMapFiles is similar to CleanupFixtures, but it takes an additional argument mapFileRelativePath, which is the path to the map file that should be cleaned up. This is useful for tests that create a map file and then need to clean it up after the test has finished.

Note that CleanupFixturesCheckMapFiles will wait until the map file is empty before returning. This is to ensure that the test does not finish before the cleanup has finished.

func (*BaseSuite) CleanupFixturesInNamespace

func (b *BaseSuite) CleanupFixturesInNamespace(fixturePath, namespace string, manifestNames []string)

func (*BaseSuite) ConsistentlyNoReload added in v0.9.0

func (b *BaseSuite) ConsistentlyNoReload(oldPid string, duration time.Duration)

ConsistentlyNoReload executes a check repeatedly for a duration. It t.Fatalf() if the condition ever returns false.

func (*BaseSuite) CreateFixtures

func (b *BaseSuite) CreateFixtures(fixturePath string, manifestNames []string)

CreateFixtures will create all the objects from manifests that are in the fixturePath directory They are created in the test namespace To create in a specific namespace use CreateFixturesInNamespace If manifestNames is empty, it will create all objects that are in the directory If manifestNames, it will create only the objects in manifestNames (a sublist of the files in fixturePath)

func (*BaseSuite) CreateFixturesInNamespace

func (b *BaseSuite) CreateFixturesInNamespace(fixturePath, namespace string, manifestNames []string)

func (*BaseSuite) ExpectAttachedListenerSets added in v1.1.0

func (b *BaseSuite) ExpectAttachedListenerSets(ctx context.Context, namespace, gwName string, expected int)

ExpectAttachedListenerSets waits for the number of ListenerSets a Gateway reports as attached, that is Gateway.status.attachedListenerSets. The field is optional and a pointer, so an absent one is read as zero: a Gateway that attaches none is not required to publish it at all.

func (*BaseSuite) ExpectAttachedRoute added in v0.9.0

func (b *BaseSuite) ExpectAttachedRoute(ctx context.Context, namespace, gwName, listenerName string, expectNbAttachedRoutes int32)

func (*BaseSuite) ExpectBackends

func (b *BaseSuite) ExpectBackends(ctx context.Context, expectationPath string, expectedBackends []string)

func (*BaseSuite) ExpectBackendsDoNotExist

func (b *BaseSuite) ExpectBackendsDoNotExist(ctx context.Context, backendThatShouldNotExist string)

func (*BaseSuite) ExpectCertificates

func (b *BaseSuite) ExpectCertificates(ctx context.Context, expectedCerts []*models.SslCertificate)

For now: only checks the following certificate fields: - StorageName - Subject (for CN) Check is performed using the RUNTIME command on haproxy

func (*BaseSuite) ExpectCrtLists

func (b *BaseSuite) ExpectCrtLists(ctx context.Context, expectedCrtLists map[futils.FilePath][]string)

ExpectCrtLists checks that the crt-list are the expectedCrtLists For now, there is no CN method to get the content of the crt-list : "show ssl crt-list <filename>" The only existing method in CN is "show ssl crt-list" that gives the list of crt-lists So, for now, we check the content of the crt-list from the crt-list file, not from the RUNTIME.

func (*BaseSuite) ExpectCrtListsIdenticalModuloNaming added in v1.1.0

func (b *BaseSuite) ExpectCrtListsIdenticalModuloNaming(templateA, templateB string, differences ...NamingDifference)

ExpectCrtListsIdenticalModuloNaming fails unless the two frontends serve the same certificates, once the ports and the declared naming differences are canonicalised.

The crt-list is where termination is configured: for a case whose two sides terminate with a secret each, comparing the two lists is comparing what a TLS handshake on either port will offer. The paths they hold carry the secret's own namespace and name, so the two sides differ there by construction -- two Secrets cannot share a name in one namespace -- and the caller declares that pair like any other naming.

Both lists are required to be non-empty first: two frontends that serve nothing must not pass by being equally empty.

func (*BaseSuite) ExpectFrontends

func (b *BaseSuite) ExpectFrontends(ctx context.Context, expectationPath string, expectedFrontends []string)

func (*BaseSuite) ExpectFrontendsIdenticalModuloNaming added in v1.1.0

func (b *BaseSuite) ExpectFrontendsIdenticalModuloNaming(templateA, templateB string, differences ...NamingDifference)

ExpectFrontendsIdenticalModuloNaming fails unless the two frontends the template names designate hold the same configuration, once the ports and the declared naming differences are canonicalised.

func (*BaseSuite) ExpectListenerRouteMapContents added in v1.0.3

func (*BaseSuite) ExpectListenerRouteMapContents(_ string)

ExpectListenerRouteMapContents is a no-op: listener maps are now per-frontend and are verified by ExpectMapContents for each frontend directory.

func (*BaseSuite) ExpectListenerSetConditions added in v1.1.0

func (b *BaseSuite) ExpectListenerSetConditions(ctx context.Context, namespace, name string, expected []metav1.Condition)

ExpectListenerSetConditions waits for the ListenerSet's own status.conditions.

func (*BaseSuite) ExpectListenerSetDeleted added in v1.1.0

func (b *BaseSuite) ExpectListenerSetDeleted(ctx context.Context, namespace, name string)

ExpectListenerSetDeleted waits for the ListenerSet to be gone. What the deletion leaves behind -- the frontend that survives, the map entries that do not -- is asserted separately, by the map and frontend expectations.

func (*BaseSuite) ExpectListenerSetEntries added in v1.1.0

func (b *BaseSuite) ExpectListenerSetEntries(ctx context.Context, namespace, lsetName string, expected []string)

ExpectListenerSetEntries waits for the exact set of entry names a ListenerSet reports, which is how a case asserts that an entry reported as invalid is still reported -- the API asks for an entry per listener, not per valid listener.

func (*BaseSuite) ExpectListenerSetEntryAttachedRoutes added in v1.1.0

func (b *BaseSuite) ExpectListenerSetEntryAttachedRoutes(ctx context.Context, namespace, lsetName, entryName string, expected int32)

ExpectListenerSetEntryAttachedRoutes waits for the attachedRoutes an entry reports.

func (*BaseSuite) ExpectListenerSetEntryConditions added in v1.1.0

func (b *BaseSuite) ExpectListenerSetEntryConditions(ctx context.Context, namespace, lsetName, entryName string, expected []metav1.Condition)

ExpectListenerSetEntryConditions waits for the conditions of one entry of a ListenerSet, that is status.listeners[].conditions, addressed by entry name. The set is compared exactly.

func (*BaseSuite) ExpectListenerSetEntryHasCondition added in v1.1.0

func (b *BaseSuite) ExpectListenerSetEntryHasCondition(ctx context.Context, namespace, lsetName, entryName string, expected metav1.Condition)

ExpectListenerSetEntryHasCondition waits for one condition of an entry to be present, whatever the entry's other conditions are.

func (*BaseSuite) ExpectListenerSetReportsNothing added in v1.1.0

func (b *BaseSuite) ExpectListenerSetReportsNothing(ctx context.Context, namespace, name string)

ExpectListenerSetReportsNothing waits for a ListenerSet to carry nothing but the conditions the API itself seeded, and no entry at all.

"Nothing published" cannot be asserted as an absence of conditions: the ListenerSet CRD declares a default status of Accepted and Programmed, both Unknown with the reason Pending and the message "Waiting for controller", so a freshly created ListenerSet already carries them and a controller that stays silent leaves them exactly as they are. Silence is therefore observable as those placeholders surviving untouched, which is also a sharper assertion than an absence: any write of ours moves observedGeneration to the object's generation and replaces the reason.

As with any assertion of silence, the case has to establish that the object was processed: pair it with a second ListenerSet whose status is published, and wait for that one first.

func (*BaseSuite) ExpectMapContents added in v0.9.0

func (b *BaseSuite) ExpectMapContents(mapFilePath, expectedMapPath string)

func (*BaseSuite) ExpectMapEntryMentions added in v1.1.0

func (b *BaseSuite) ExpectMapEntryMentions(mapFileRelativePath, key string, mustMention, mustNotMention []string)

ExpectMapEntryMentions waits for the line of a map file whose key is `key` to mention every value of mustMention and none of mustNotMention.

What it asserts is attribution, which is what a value carries when several resources share a port: the frontends are keyed on the port, so a listener that moves from one Gateway to another keeps its line in the same map -- under the new Gateway's key. Asserting the entry's absence there would be asserting the frontend had gone, which is a different question.

func (*BaseSuite) ExpectMapsIdenticalModuloNaming added in v1.1.0

func (b *BaseSuite) ExpectMapsIdenticalModuloNaming(templateA, templateB string, differences ...NamingDifference)

ExpectMapsIdenticalModuloNaming fails unless every standard map of the two frontends holds the same entries, once the ports and the declared naming differences are canonicalised.

The maps are where the routing lives: the hostname-to-listener table, the listener/route table and the SNI table are what a request is resolved through, and comparing them is comparing what the two listeners actually serve.

The listener maps are required to be non-empty on both sides first, so that two frontends which programmed nothing cannot pass by being equally empty.

func (*BaseSuite) ExpectNoMapEntry added in v1.1.0

func (b *BaseSuite) ExpectNoMapEntry(mapFileRelativePath, value string)

ExpectNoMapEntry waits for no line of a map file to mention the given value.

This is what a purge is asserted with, on a frontend that survives: reading the file tells the entries a deleted object had filled apart from a frontend that was simply destroyed with its maps.

func (*BaseSuite) ExpectRouteConditionsUpdated added in v0.9.0

func (b *BaseSuite) ExpectRouteConditionsUpdated(ctx context.Context, namespace, name string, expectedConditions rc.RouteConditions)

func (*BaseSuite) ExpectRouteHasNoParent added in v1.1.0

func (b *BaseSuite) ExpectRouteHasNoParent(ctx context.Context, namespace, routeName string, parent gatewayv1.ParentReference)

ExpectRouteHasNoParent waits for a route to publish no status for a given parent.

It is the mirror of ExpectRouteParentHasCondition and has the same blind spot in reverse: "no entry" is true before the controller has looked at the route, so a case that asserts it must first wait for another parent of the same route to be published. The pair is what makes the absence a statement about the controller rather than about the test's timing.

func (*BaseSuite) ExpectRouteParentHasCondition added in v1.1.0

func (b *BaseSuite) ExpectRouteParentHasCondition(ctx context.Context, namespace, routeName string, parent gatewayv1.ParentReference, expected metav1.Condition)

ExpectRouteParentHasCondition waits for one route to carry a condition on a given parent, and asserts it by presence rather than by set.

The exact set a route publishes for a parent is assembled by two passes -- the parentRef check, then the resolution of its rules' references -- so which conditions accompany the one asserted here is not something a caller should have to know. The parent is identified by kind, name and sectionName, which is also what says *which* parent the route blamed: a refusal that names a ListenerSet is a different claim from one that names its Gateway.

func (*BaseSuite) ExpectServers added in v0.9.0

func (b *BaseSuite) ExpectServers(backend string, expectedServers []string)

func (*BaseSuite) FrontendFromManifest

func (b *BaseSuite) FrontendFromManifest(manifestPath, manifestName string) *models.Frontend

func (*BaseSuite) GetMapFileFrom added in v0.8.0

func (b *BaseSuite) GetMapFileFrom(mapFileRelativePath string) ([]string, error)

func (*BaseSuite) IntTest added in v1.0.9

func (b *BaseSuite) IntTest() IntTest

func (*BaseSuite) SetupSuite

func (b *BaseSuite) SetupSuite(crdRelativePath string, levelsUp int, opts ...func(*IntTest))

func (*BaseSuite) TearDownSuite

func (b *BaseSuite) TearDownSuite()

func (*BaseSuite) WaitForNoReloadsAnyMore added in v0.9.0

func (b *BaseSuite) WaitForNoReloadsAnyMore(
	overAllDuration time.Duration,
	consistentlyDurationWithoutReloads time.Duration,
) string

WaitForNoReloadsAnyMore checks during an overall overAllDuration That we reach a stable state without reloads for at least consistentlyDurationWithoutReloads We set a timer stabilityTimer and if at any point during the stability window a reload occurs, we reset this timer. If we reach the overAllDuration without a stable window without reload we issue a t.Fatalf()

func (*BaseSuite) YamlToConditions

func (b *BaseSuite) YamlToConditions(yamlPath string) conditions.Conditions

func (*BaseSuite) YamlToListenerStatuses

func (b *BaseSuite) YamlToListenerStatuses(yamlPath string) []gatewayv1.ListenerStatus

func (*BaseSuite) YamlToRouteConditions

func (b *BaseSuite) YamlToRouteConditions(yamlPath string) rc.RouteConditions

type IntTest

type IntTest struct {
	Ctx           context.Context
	Client        ctrlruntimeclient.Client
	RuntimeClient runtime.Runtime
	HaproxyClient hapapi.HAProxyClient
	TestEnv       *envtest.Environment

	PortMap           map[int]int // template port → unique ephemeral port for this test run
	Namespace         string
	HaproxyCfgDir     string
	RuntimeSocketPath string
	PIDFilePath       string
	// EnableIngress opts the controller under test into Kubernetes Ingress
	// support. Off by default so suites that don't exercise Ingress do not get
	// the synthetic gateway's extra frontends.
	EnableIngress bool
	// GWAPICompat switches backendless-rule responses from the legacy 404
	// to the spec-mandated 500.
	GWAPICompat bool
	// contains filtered or unexported fields
}

func NewIntTest

func NewIntTest(t *testing.T, crdRelativePath string, levelsUp int) (test IntTest, err error)

func (*IntTest) StartTestEnv

func (test *IntTest) StartTestEnv(t *testing.T)

func (*IntTest) StopHaproxy added in v0.9.1

func (test *IntTest) StopHaproxy(t *testing.T)

func (*IntTest) StopTestEnv

func (test *IntTest) StopTestEnv(t *testing.T)

type NamingDifference added in v1.1.0

type NamingDifference [2]string

NamingDifference is one difference a parity comparison tolerates: two names that denote the same thing on either side -- the resource each frontend belongs to, or the route that feeds each of them when they are distinct objects. Every member is rewritten to the same placeholder before comparing.

The first pair a caller passes is the pair of declaring resources, and both members must be present in their own document: a frontend that lost the origin it was programmed for is a defect of its own, and it must not pass by having nothing left to compare.

Jump to

Keyboard shortcuts

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