Documentation
¶
Overview ¶
Package framework provides shared test infrastructure for faros e2e tests.
Package framework provides shared test infrastructure for faros e2e tests.
Index ¶
- Constants
- Variables
- func AgentBinPath() string
- func AgentSavedKubeconfigPath(edgeName string) (string, error)
- func CallerIdentityFromContext(ctx context.Context) string
- func ClusterNameFromKubeconfig(kubeconfigPath string) string
- func DeleteOrgViaREST(ctx context.Context, hubURL, bearer, orgUUID string) (int, error)
- func DoRESTRequest(ctx context.Context, method, url, bearer string, ...) (int, []byte, error)
- func FindPersonalOrgUUID(ctx context.Context, hubURL, bearer string) (string, error)
- func GatewayGet(ctx context.Context, host, path string) (int, error)
- func GenerateTestSSHKeypair() (*rsa.PrivateKey, gossh.PublicKey, []byte, error)
- func HTTPGet(ctx context.Context, url string) (int, error)
- func HTTPGetBody(ctx context.Context, url string) (int, string, error)
- func HubNodePortURL() string
- func IndentLines(text, prefix string) string
- func InstallStateDir(repoRoot string) string
- func IsAuthRejectStatus(code int) bool
- func KubectlWithConfig(ctx context.Context, kubeconfig string, args ...string) (string, error)
- func LoadAgentImageIntoCluster(clusterName string) error
- func PodHubURLFromKubeconfig(kubeconfigPath string) string
- func Poll(ctx context.Context, interval, timeout time.Duration, condition ConditionFunc) error
- func RepoRoot() string
- func ResolveCallerIdentity(ctx context.Context, kubeconfigPath string) (string, error)
- func RunCmd(ctx context.Context, name string, args ...string) (string, error)
- func RunInstallScript(ctx context.Context, repoRoot, name string) error
- func SSHPrivateKeyPEMFromContext(ctx context.Context) []byte
- func SetupClusters(workDir string) env.Func
- func SetupClustersWithAgentCount(workDir string, agentCount int) env.Func
- func SetupClustersWithExternalKCP(workDir string) env.Func
- func SetupClustersWithOIDC(workDir string) env.Func
- func SetupInstallFlow(repoRoot string, scripts []string) env.Func
- func StartInstallPortForwards(ctx context.Context, repoRoot string) error
- func TeardownClusters(workDir string) env.Func
- func TeardownClustersWithAgentCount(workDir string, agentCount int) env.Func
- func TeardownInstallFlow(repoRoot string) env.Func
- func UseExistingClusters(workDir string) env.Func
- func UseExistingClustersWithExternalKCP(workDir string) env.Func
- func UseExistingClustersWithOIDC(workDir string) env.Func
- func WaitForAgentSavedKubeconfig(ctx context.Context, edgeName string, timeout time.Duration) (string, error)
- func WaitForDeploymentAvailable(ctx context.Context, kubeconfig, namespace, name string, timeout time.Duration) error
- func WaitForDexReady(ctx context.Context) error
- func WaitForHubReady(ctx context.Context, hubURL string) error
- func WaitForTenantAPI(ctx context.Context, client *FarosClient, hubURL, token string) error
- func WaitForTenantAPIWithOIDC(ctx context.Context, workDir, hubURL string) error
- func WithCallerIdentity(ctx context.Context, identity string) context.Context
- func WithClusterEnv(ctx context.Context, c *ClusterEnv) context.Context
- func WithDexEnv(ctx context.Context, d *DexEnv) context.Context
- func WithSSHPrivateKeyPEM(ctx context.Context, pem []byte) context.Context
- func WithServerContainer(ctx context.Context, c *ServerContainer) context.Context
- func WithServerProcess(ctx context.Context, p *ServerProcess) context.Context
- type Agent
- type AgentClusterInfo
- type ClusterEnv
- type ConditionFunc
- type CreateOrgResponse
- type CreateWorkspaceResponse
- type DexEnv
- type EdgeSSHCredentials
- type FarosClient
- func (k *FarosClient) ApplyFile(ctx context.Context, path string) error
- func (k *FarosClient) ApplyManifest(ctx context.Context, yaml string) error
- func (k *FarosClient) DeleteVirtualWorkload(ctx context.Context, name, namespace string) error
- func (k *FarosClient) EdgeCreate(ctx context.Context, name, edgeType string, labels ...string) error
- func (k *FarosClient) EdgeDelete(ctx context.Context, name string) error
- func (k *FarosClient) EdgeJoinCommand(ctx context.Context, edgeName string) (string, error)
- func (k *FarosClient) EdgeKubeconfig(ctx context.Context, edgeName, outputPath string) error
- func (k *FarosClient) EdgeList(ctx context.Context) (string, error)
- func (k *FarosClient) ExtractEdgeKubeconfig(ctx context.Context, edgeName, destPath string) error
- func (k *FarosClient) GetEdgeCondition(ctx context.Context, edgeName, conditionType string) (string, error)
- func (k *FarosClient) GetEdgeJoinToken(ctx context.Context, edgeName string) (string, error)
- func (k *FarosClient) GetEdgeSSHCredentials(ctx context.Context, edgeName string) (*EdgeSSHCredentials, error)
- func (k *FarosClient) GetEdgeURL(ctx context.Context, name string) (string, error)
- func (k *FarosClient) Kubectl(ctx context.Context, args ...string) (string, error)
- func (k *FarosClient) KubectlWithURL(ctx context.Context, serverURL string, args ...string) (string, error)
- func (k *FarosClient) Login(ctx context.Context, token string) error
- func (k *FarosClient) Run(ctx context.Context, args ...string) (string, error)
- func (k *FarosClient) WaitForEdgeCondition(ctx context.Context, edgeName, conditionType, expectedStatus string, ...) error
- func (k *FarosClient) WaitForEdgeJoinToken(ctx context.Context, edgeName string, timeout time.Duration) (string, error)
- func (k *FarosClient) WaitForEdgeJoinTokenCleared(ctx context.Context, edgeName string, timeout time.Duration) error
- func (k *FarosClient) WaitForEdgeKubeconfig(ctx context.Context, edgeName, outputPath string, timeout time.Duration) error
- func (k *FarosClient) WaitForEdgePhase(ctx context.Context, edgeName, phase string, timeout time.Duration) error
- func (k *FarosClient) WaitForEdgeReady(ctx context.Context, edgeName string, timeout time.Duration) error
- func (k *FarosClient) WaitForEdgeSSHCredentials(ctx context.Context, edgeName string, timeout time.Duration) (*EdgeSSHCredentials, error)
- func (k *FarosClient) WaitForNoPlacement(ctx context.Context, vwName, namespace, edgeName string, timeout time.Duration) error
- func (k *FarosClient) WaitForPlacement(ctx context.Context, vwName, namespace, edgeName string, timeout time.Duration) error
- type OIDCLoginResult
- type SAResponse
- type SSHWebSocketClient
- type ServerContainer
- type ServerProcess
- type TestSSHServer
- type TokenAgent
- func (a *TokenAgent) Start(ctx context.Context) error
- func (a *TokenAgent) Stop()
- func (a *TokenAgent) WithAgentKubeconfig(kc string) *TokenAgent
- func (a *TokenAgent) WithCluster(clusterName string) *TokenAgent
- func (a *TokenAgent) WithSSHPassword(pass string) *TokenAgent
- func (a *TokenAgent) WithSSHUser(user string) *TokenAgent
- func (a *TokenAgent) WithType(t string) *TokenAgent
- type TokenResponse
Constants ¶
const ( DefaultHubClusterName = "faros-e2e-hub" DefaultAgentClusterName = "faros-e2e-agent" DefaultKindNetwork = "faros-e2e" DefaultChartPath = "deploy/charts/faros-hub" DefaultHubURL = "https://faros.localhost:9443" // DefaultAgentCount is the number of agent clusters created by the e2e // test suites. All suites create 2 agent clusters so multi-site tests run // in every flavour. DefaultAgentCount = 2 )
const ( // DexServicePort is the host port where Dex is reachable from the test // runner via the kind port mapping. The dexidp Helm chart binds HTTPS // to containerPort 5554 (hard-coded --web-https-addr), so we expose // the same number on the host for consistency. DexServicePort = 5554 // DexIssuerURL is the OIDC issuer URL used by both the hub pod (cluster // DNS) and the test runner (/etc/hosts alias to localhost). HTTPS so // embedded kcp's authentication validator (which mandates scheme=https) // accepts it; the cert is signed by the faros-selfsigned ClusterIssuer // and clients use InsecureSkipVerify. DexIssuerURL = "https://dex.faros-system.svc.cluster.local:5554/dex" // DexExternalHost is added to the test runner's /etc/hosts as 127.0.0.1 // so it can reach the in-cluster Dex via the kind port mapping. DexExternalHost = "dex.faros-system.svc.cluster.local" // DexClientID / DexClientSecret are the OAuth2 credentials for the hub. DexClientID = "faros" DexClientSecret = "faros-test-secret" // DexTestUserEmail / DexTestUserPassword are the static-password credentials // seeded in Dex for e2e OIDC tests (primary user / User A). DexTestUserEmail = "admin@test.faros.local" DexTestUserPassword = "Password1!" // DexTestUser2Email / DexTestUser2Password are the credentials for the second // Dex static-password user, used in cross-user isolation tests (issue #79). DexTestUser2Email = "user2@test.faros.local" DexTestUser2Password = "Password1!" )
const ( // DefaultInstallClusterName is the kind cluster the install suites create. // Distinct from the docs default ("faros") so a developer's manual // walkthrough and an e2e run never fight over the same cluster. DefaultInstallClusterName = "faros-e2e-install" // InstallStateDirName is the state directory (extracted kubeconfigs, // port-forward pidfiles) the install suites pass to the scripts. InstallStateDirName = ".faros-install-e2e" // InstallGatewayAddr is the local address of the Envoy gateway // port-forward started by hack/install/port-forward.sh. InstallGatewayAddr = "127.0.0.1:8443" // InstallHubURL matches the hack/install default HUB_EXTERNAL_URL. Plain // localhost (not faros.localhost): *.localhost subdomains don't resolve on // stock macOS, and the install flow should run anywhere the docs do. InstallHubURL = "https://localhost:9443" )
const ( // ContainerSSHImage is the Docker image used for the container-based SSH // server test. We use a plain Ubuntu image and install openssh-server at // container start time so that we have full control over the sshd // configuration (in particular: PermitRootLogin + PermitEmptyPasswords). ContainerSSHImage = "ubuntu:22.04" // ContainerSSHPort is the port sshd listens on inside the container. // We use 2222 to avoid conflicts with any host sshd on port 22. ContainerSSHPort = 2222 )
const DefaultKCPExternalKubeconfigFile = "kcp-admin.kubeconfig"
DefaultKCPExternalKubeconfigFile is the filename written by faros dev init --with-external-kcp for the test runner to reach kcp directly.
const ( // DefaultTestSSHPort is the port used by the embedded test SSH server. // High enough to be unprivileged and unlikely to conflict on CI runners. DefaultTestSSHPort = 2222 )
Variables ¶
var ( // SSHKeepaliveDuration is how long the long-lived SSH connection test holds the // session open before asserting liveness. // // Minimum useful value: 60s. The long_lived_connection_stays_alive test uses a // 30-second keepalive ticker; setting the duration below 60s means the ticker // never fires during the hold period and the keepalive logic is never exercised. // Default 60s; bump to 10m+ locally to stress-test keepalive behaviour. SSHKeepaliveDuration time.Duration // KeepClusters controls whether kind clusters are deleted after the test run. // Default is false (clusters are deleted). Set --keep-clusters to retain them // for debugging failures. KeepClusters bool // FarosBin is the path to the faros binary under test. FarosBin string // DevToken is the static auth token used in standalone (non-OIDC) test suites. DevToken string )
var RESTClient = &http.Client{ Timeout: 15 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, }
RESTClient is a TLS-skip HTTP client targeting the hub's self-signed dev cert. Shared across tenancy cases.
Functions ¶
func AgentBinPath ¶
func AgentBinPath() string
AgentBinPath returns the path to the faros binary under bin/.
func AgentSavedKubeconfigPath ¶
AgentSavedKubeconfigPath returns the filesystem path where the agent binary persists the kubeconfig received via token-exchange. It mirrors the logic in pkg/agent.AgentKubeconfigPath so tests can verify the file was written.
func CallerIdentityFromContext ¶
CallerIdentityFromContext retrieves the expected caller identity from the context.
func ClusterNameFromKubeconfig ¶
ClusterNameFromKubeconfig reads the kubeconfig at path and extracts the kcp cluster name from the server URL (e.g. "https://hub:9443/clusters/abc123" → "abc123"). Returns "" when the kubeconfig cannot be read or has no cluster path, so callers should skip passing --cluster in that case.
func DeleteOrgViaREST ¶
DeleteOrgViaREST soft-deletes an Org (best-effort; non-fatal).
func DoRESTRequest ¶
func DoRESTRequest( ctx context.Context, method, url, bearer string, tenantHeaders map[string]string, body any, ) (int, []byte, error)
DoRESTRequest performs a JSON request against the hub REST surface and returns the status code + response body. body may be nil for GET/DELETE. tenantHeaders carries X-Faros-Org / X-Faros-Workspace as needed.
func FindPersonalOrgUUID ¶
FindPersonalOrgUUID returns the personal-org UUID for the caller. Used by personal-org guardrail tests.
func GatewayGet ¶ added in v0.1.11
GatewayGet performs an HTTPS GET through the local Envoy gateway port-forward, dialing InstallGatewayAddr while presenting the given SNI hostname — exactly what a client resolving <host> to the gateway would do. Returns the HTTP status code.
func GenerateTestSSHKeypair ¶
GenerateTestSSHKeypair generates a 2048-bit RSA keypair for use in e2e tests. It returns the RSA private key, the SSH public key, and the PEM-encoded private key bytes (PKCS#1 RSA PRIVATE KEY format accepted by gossh.ParsePrivateKey).
func HTTPGet ¶
HTTPGet performs a GET to url using an insecure client (self-signed certs in dev) and returns the HTTP status code.
func HTTPGetBody ¶
HTTPGetBody performs a GET to url and returns the status code and response body. TLS verification is skipped — used for in-cluster services exposed via kind port mapping with self-signed certs (Dex, hub).
func HubNodePortURL ¶
func HubNodePortURL() string
HubNodePortURL returns the hub URL reachable from inside a pod in another kind cluster — i.e. via the hub node's Docker IP on the shared kind network and NodePort 31443. This is needed because faros.localhost resolves only on the CI runner host, not inside pods. Returns "" if the Docker IP cannot be determined (caller should skip or fall back).
func IndentLines ¶
IndentLines prepends prefix to every non-empty line in text. Used for embedding PEM blocks in YAML manifests.
func InstallStateDir ¶ added in v0.1.11
InstallStateDir returns the state directory used by the install suites.
func IsAuthRejectStatus ¶
IsAuthRejectStatus is true for any status code the REST surface is allowed to return when an identity is denied. The hub may pick 401 (no/invalid auth), 403 (auth ok, not a member), or 404 (refuse to confirm existence) depending on which check fires first. All three are acceptable for negative checks; 200/2xx is the bug.
func KubectlWithConfig ¶
KubectlWithConfig runs kubectl with an explicit kubeconfig path (package-level helper).
func LoadAgentImageIntoCluster ¶
LoadAgentImageIntoCluster loads the agent container image into a kind cluster so that Deployments with imagePullPolicy=Never can use it without a registry pull. This is a no-op unless FAROS_AGENT_IMAGE_PULL_POLICY=Never (i.e. CI with a locally built image).
func PodHubURLFromKubeconfig ¶
PodHubURLFromKubeconfig returns the hub URL reachable from inside a pod in the agent kind cluster. It reads the server URL from the hub kubeconfig (which contains the full /clusters/<name> path), then replaces the host with the hub node's Docker network NodePort address so that in-cluster agents can connect.
clusterEnv.HubURL is always "https://faros.localhost:9443" (no cluster path), so this function reads the cluster path directly from the hub kubeconfig instead of relying on the HubURL field.
Returns "" if the hub kubeconfig cannot be read or the NodePort address is unavailable.
func Poll ¶
Poll calls condition repeatedly with the given interval until it returns (true, nil) or the context is done. Returns an error if the context expires or condition returns a non-nil error.
func RepoRoot ¶
func RepoRoot() string
RepoRoot returns the absolute path to the faros repository root, derived from the location of this source file at compile time.
func ResolveCallerIdentity ¶
ResolveCallerIdentity performs a TokenReview against the hub to discover the username associated with the hub kubeconfig's bearer token. Returns an empty string (without error) if the token is unauthenticated or the server does not support TokenReview.
func RunCmd ¶
RunCmd runs an arbitrary command and returns its combined output (package-level helper).
func RunInstallScript ¶ added in v0.1.11
RunInstallScript executes one hack/install/<name> script, streaming output.
func SSHPrivateKeyPEMFromContext ¶
SSHPrivateKeyPEMFromContext retrieves a PEM-encoded SSH private key from the context.
func SetupClusters ¶
SetupClusters returns an env.Func that creates the hub and agent kind clusters using `faros dev init` with the local Helm chart. It stores a ClusterEnv in the context for use by tests.
If the FAROS_HUB_IMAGE_PULL_POLICY env var is set (e.g. to "Never" in CI when the image is pre-loaded into kind), it is forwarded to `faros dev init`.
func SetupClustersWithAgentCount ¶
SetupClustersWithAgentCount is like SetupClusters but creates agentCount agent clusters instead of DefaultAgentCount. Use agentCount=1 for suites that do not need multi-agent tests (e.g. SSH) to save cluster creation time.
func SetupClustersWithExternalKCP ¶
SetupClustersWithExternalKCP returns an env.Func that creates hub and agent kind clusters using `faros dev init --with-external-kcp`. kcp is deployed via Helm into the hub cluster; the hub is configured to use it.
The external kcp kubeconfig (for test assertions against kcp directly) is stored in ClusterEnv.KCPKubeconfig.
func SetupClustersWithOIDC ¶
SetupClustersWithOIDC is like SetupClusters but also deploys Dex as an OIDC provider inside the hub kind cluster (via --with-dex).
Networking: Dex is exposed as NodePort 31554 on the hub kind node; the kind cluster maps that to localhost:5554. The test runner adds a /etc/hosts entry (127.0.0.1 dex.faros-system.svc.cluster.local) so it can reach the in-cluster Dex on the same hostname that the hub pod uses via cluster DNS.
func SetupInstallFlow ¶ added in v0.1.11
SetupInstallFlow returns an env.Func that runs the given hack/install scripts in order, starts the port-forwards, waits for the hub and tenant API, and stores a ClusterEnv. Pass the exact script list a doc prescribes, e.g. external: 01,02,03,04,05,06,07 — embedded: 01,03,08.
func StartInstallPortForwards ¶ added in v0.1.11
StartInstallPortForwards (re)starts the gateway + hub port-forwards.
func TeardownClusters ¶
TeardownClusters returns an env.Func that deletes the hub and agent kind clusters unless KeepClusters is set.
func TeardownClustersWithAgentCount ¶
TeardownClustersWithAgentCount is TeardownClusters for a custom agent count.
func TeardownInstallFlow ¶ added in v0.1.11
TeardownInstallFlow deletes the install kind cluster and state via hack/install/teardown.sh, honouring --keep-clusters.
func UseExistingClusters ¶
UseExistingClusters wires up ClusterEnv from already-running clusters without creating or destroying anything. It verifies that the hub is healthy before returning. Cluster names can be overridden via FAROS_HUB_CLUSTER_NAME and FAROS_AGENT_CLUSTER_NAME.
func UseExistingClustersWithExternalKCP ¶
UseExistingClustersWithExternalKCP is the FAROS_USE_EXISTING_CLUSTERS=true variant of SetupClustersWithExternalKCP. It assumes clusters and kcp are already running and just wires up the ClusterEnv.
func UseExistingClustersWithOIDC ¶
UseExistingClustersWithOIDC wires up ClusterEnv and DexEnv from already-running clusters (FAROS_USE_EXISTING_CLUSTERS=true path). It verifies that the hub and Dex are reachable but does NOT create or destroy any clusters.
Cluster names can be overridden via FAROS_HUB_CLUSTER_NAME and FAROS_AGENT_CLUSTER_NAME environment variables. This is useful when testing against the dev cluster (faros-hub / faros-agent) instead of the e2e cluster.
func WaitForAgentSavedKubeconfig ¶
func WaitForAgentSavedKubeconfig(ctx context.Context, edgeName string, timeout time.Duration) (string, error)
WaitForAgentSavedKubeconfig polls until the saved kubeconfig file appears at the expected path, or until timeout expires.
func WaitForDeploymentAvailable ¶
func WaitForDeploymentAvailable(ctx context.Context, kubeconfig, namespace, name string, timeout time.Duration) error
WaitForDeploymentAvailable polls kubectl until the named Deployment in the given namespace has availableReplicas >= 1, or the timeout expires. kubeconfig is the path to the kubeconfig for the cluster hosting the Deployment.
func WaitForDexReady ¶
WaitForDexReady polls Dex's OIDC discovery endpoint on localhost until it returns 200 or the context deadline is exceeded. The test runner reaches Dex via the kind port mapping on localhost:DexServicePort. Dex serves TLS with a cert valid for the in-cluster name; the client skips verification.
func WaitForHubReady ¶
WaitForHubReady polls the hub's /healthz endpoint until it returns 200 or the context deadline is exceeded.
func WaitForTenantAPI ¶
func WaitForTenantAPI(ctx context.Context, client *FarosClient, hubURL, token string) error
WaitForTenantAPI logs in with a static token and polls the hub's token-login endpoint until the tenant/users APIBinding has finished bootstrapping. Until then the hub can 500 ("failed to create user") on the first tenant operations, so suites gate startup on this.
This replaces the pre-decouple WaitForEdgeAPI gate: edges are now an optional out-of-process provider (group edges.faros.sh) that these suites do not bootstrap, so "edge list works" is no longer a valid readiness signal. Edge connectivity has its own dedicated suite.
func WaitForTenantAPIWithOIDC ¶
WaitForTenantAPIWithOIDC is like WaitForTenantAPI but proves readiness via a headless OIDC login (users APIBinding must be bound for the login handler to mint a user). Used when the hub runs in OIDC-only mode (--with-dex).
func WithCallerIdentity ¶
WithCallerIdentity stores the expected caller identity in the context.
func WithClusterEnv ¶
func WithClusterEnv(ctx context.Context, c *ClusterEnv) context.Context
WithClusterEnv stores a ClusterEnv in the context.
func WithDexEnv ¶
WithDexEnv stores DexEnv in a context.
func WithSSHPrivateKeyPEM ¶
WithSSHPrivateKeyPEM stores a PEM-encoded SSH private key in the context.
func WithServerContainer ¶
func WithServerContainer(ctx context.Context, c *ServerContainer) context.Context
WithServerContainer stores a ServerContainer in the context.
func WithServerProcess ¶
func WithServerProcess(ctx context.Context, p *ServerProcess) context.Context
WithServerProcess stores a ServerProcess in the context.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent manages a faros-agent process for e2e tests.
type AgentClusterInfo ¶
AgentClusterInfo holds the name and kubeconfig path for a single agent cluster.
type ClusterEnv ¶
type ClusterEnv struct {
HubClusterName string
HubKubeconfig string
HubURL string
Token string
WorkDir string
// AgentClusters holds all agent clusters in creation order.
// Use AgentClusters[0] for single-agent tests (backwards-compat).
AgentClusters []AgentClusterInfo
// AgentClusterName and AgentKubeconfig are shims for AgentClusters[0]
// kept for backwards compatibility with existing single-agent test cases.
AgentClusterName string
AgentKubeconfig string
// KCPKubeconfig is the path to the external kcp admin kubeconfig written
// to WorkDir. Only populated by SetupClustersWithExternalKCP.
KCPKubeconfig string
// HubAdminKubeconfig is the raw kind-cluster admin kubeconfig for the hub
// cluster, saved before faros login overwrites the main HubKubeconfig with
// a kcp workspace context. Use this for kubectl commands against the hub
// kind cluster itself (e.g. deleting pods in the kcp namespace).
HubAdminKubeconfig string
}
ClusterEnv holds runtime paths and names for a test cluster environment.
func ClusterEnvFrom ¶
func ClusterEnvFrom(ctx context.Context) *ClusterEnv
ClusterEnvFrom retrieves a ClusterEnv from the context.
type ConditionFunc ¶
ConditionFunc is a function that returns (done bool, err error). A nil error with done=false means retry; a non-nil error stops polling.
type CreateOrgResponse ¶
type CreateOrgResponse struct {
UUID string `json:"uuid"`
DisplayName string `json:"displayName"`
Personal bool `json:"personal"`
}
func CreateOrgViaREST ¶
func CreateOrgViaREST(ctx context.Context, hubURL, bearer, displayName string) (CreateOrgResponse, error)
CreateOrgViaREST creates a non-personal Organization under the given bearer identity and returns the created UUID. Fatal on non-201.
type CreateWorkspaceResponse ¶
type CreateWorkspaceResponse struct {
UUID string `json:"uuid"`
OrgUUID string `json:"orgUUID"`
DisplayName string `json:"displayName,omitempty"`
}
func CreateWorkspaceViaREST ¶
func CreateWorkspaceViaREST(ctx context.Context, hubURL, bearer, orgUUID, displayName string) (CreateWorkspaceResponse, error)
CreateWorkspaceViaREST creates a Workspace under an Organization.
type DexEnv ¶
type DexEnv struct {
IssuerURL string
ClientID string
ClientSecret string
// UserEmail / UserPassword are credentials for the primary test user (User A).
UserEmail string
UserPassword string
// User2Email / User2Password are credentials for the secondary test user (User B).
// Used in cross-user isolation tests to verify that User B cannot access
// resources owned by User A.
User2Email string
User2Password string
}
DexEnv holds runtime OIDC provider info stored in the test context.
func DefaultDexEnv ¶
func DefaultDexEnv() *DexEnv
DefaultDexEnv returns the DexEnv used in the e2e OIDC suite.
func DexEnvFrom ¶
DexEnvFrom retrieves DexEnv from a context.
type EdgeSSHCredentials ¶
type EdgeSSHCredentials struct {
Username string
PasswordSecretRef string // "<namespace>/<name>" or "" if not set
PrivateKeySecretRef string // "<namespace>/<name>" or "" if not set
}
EdgeSSHCredentials holds the SSH credentials observed on an edge status.
type FarosClient ¶
type FarosClient struct {
// contains filtered or unexported fields
}
FarosClient wraps the faros CLI binary for use in e2e tests.
func NewFarosClient ¶
func NewFarosClient(workDir, kubeconfig, hubURL string) *FarosClient
NewFarosClient creates a new FarosClient.
func (*FarosClient) ApplyFile ¶
func (k *FarosClient) ApplyFile(ctx context.Context, path string) error
ApplyFile applies a YAML file via kubectl against the hub kubeconfig.
func (*FarosClient) ApplyManifest ¶
func (k *FarosClient) ApplyManifest(ctx context.Context, yaml string) error
ApplyManifest writes yaml to a temp file and applies it via kubectl.
func (*FarosClient) DeleteVirtualWorkload ¶
func (k *FarosClient) DeleteVirtualWorkload(ctx context.Context, name, namespace string) error
DeleteVirtualWorkload deletes a VirtualWorkload by name and namespace.
func (*FarosClient) EdgeCreate ¶
func (k *FarosClient) EdgeCreate(ctx context.Context, name, edgeType string, labels ...string) error
EdgeCreate creates an Edge resource via kubectl with the given name, type, and optional comma-separated labels. type must be "kubernetes" or "server".
func (*FarosClient) EdgeDelete ¶
func (k *FarosClient) EdgeDelete(ctx context.Context, name string) error
EdgeDelete deletes an Edge resource by name.
func (*FarosClient) EdgeJoinCommand ¶
EdgeJoinCommand runs `faros edge join-command <name>` and returns the printed output.
func (*FarosClient) EdgeKubeconfig ¶
func (k *FarosClient) EdgeKubeconfig(ctx context.Context, edgeName, outputPath string) error
EdgeKubeconfig runs `faros kubeconfig edge <name> --output <path>`.
func (*FarosClient) EdgeList ¶
func (k *FarosClient) EdgeList(ctx context.Context) (string, error)
EdgeList returns raw kubectl output for listing all edges.
func (*FarosClient) ExtractEdgeKubeconfig ¶
func (k *FarosClient) ExtractEdgeKubeconfig(ctx context.Context, edgeName, destPath string) error
ExtractEdgeKubeconfig waits for the edge kubeconfig secret to appear in the hub cluster and writes the base64-decoded content to destPath. Secret name format: edge-<edgeName>-kubeconfig in namespace faros-system.
func (*FarosClient) GetEdgeCondition ¶
func (k *FarosClient) GetEdgeCondition(ctx context.Context, edgeName, conditionType string) (string, error)
GetEdgeCondition returns the status (True/False/Unknown) of the named condition on the edge, or "" if the condition is not present.
func (*FarosClient) GetEdgeJoinToken ¶
GetEdgeJoinToken returns the current value of edge.status.joinToken. Returns an empty string (no error) when the field is not yet populated.
func (*FarosClient) GetEdgeSSHCredentials ¶
func (k *FarosClient) GetEdgeSSHCredentials(ctx context.Context, edgeName string) (*EdgeSSHCredentials, error)
GetEdgeSSHCredentials returns the current status.sshCredentials for an edge. Returns nil (no error) when the field is not yet set.
func (*FarosClient) GetEdgeURL ¶
GetEdgeURL polls until edge.status.URL is populated and returns it. It returns an error if the URL is not set within 2 minutes.
func (*FarosClient) KubectlWithURL ¶
func (k *FarosClient) KubectlWithURL(ctx context.Context, serverURL string, args ...string) (string, error)
KubectlWithURL runs kubectl against a specific server URL using credentials from the hub kubeconfig. The hub bearer token is passed transparently to the edge proxy endpoint on the hub.
func (*FarosClient) Login ¶
func (k *FarosClient) Login(ctx context.Context, token string) error
Login authenticates to the hub using a static token.
func (*FarosClient) Run ¶
Run executes an arbitrary faros command and returns stdout+stderr combined. This is the public variant of the internal run() helper.
func (*FarosClient) WaitForEdgeCondition ¶
func (k *FarosClient) WaitForEdgeCondition(ctx context.Context, edgeName, conditionType, expectedStatus string, timeout time.Duration) error
WaitForEdgeCondition polls until edge condition conditionType reaches the expected status (e.g. "True"), or returns an error after timeout.
func (*FarosClient) WaitForEdgeJoinToken ¶
func (k *FarosClient) WaitForEdgeJoinToken(ctx context.Context, edgeName string, timeout time.Duration) (string, error)
WaitForEdgeJoinToken polls until edge.status.joinToken is set and returns it. Returns an error if the token is not populated within timeout.
func (*FarosClient) WaitForEdgeJoinTokenCleared ¶
func (k *FarosClient) WaitForEdgeJoinTokenCleared(ctx context.Context, edgeName string, timeout time.Duration) error
WaitForEdgeJoinTokenCleared polls until edge.status.joinToken is empty (cleared after successful registration). Returns an error if the field is still non-empty after timeout.
func (*FarosClient) WaitForEdgeKubeconfig ¶
func (k *FarosClient) WaitForEdgeKubeconfig(ctx context.Context, edgeName, outputPath string, timeout time.Duration) error
WaitForEdgeKubeconfig polls until EdgeKubeconfig successfully writes to outputPath.
func (*FarosClient) WaitForEdgePhase ¶
func (k *FarosClient) WaitForEdgePhase(ctx context.Context, edgeName, phase string, timeout time.Duration) error
WaitForEdgePhase polls until the given Edge resource has the expected phase.
func (*FarosClient) WaitForEdgeReady ¶
func (k *FarosClient) WaitForEdgeReady(ctx context.Context, edgeName string, timeout time.Duration) error
WaitForEdgeReady polls until the given Edge resource has phase "Ready".
func (*FarosClient) WaitForEdgeSSHCredentials ¶
func (k *FarosClient) WaitForEdgeSSHCredentials(ctx context.Context, edgeName string, timeout time.Duration) (*EdgeSSHCredentials, error)
WaitForEdgeSSHCredentials polls until edge.status.sshCredentials.username is non-empty and returns the credentials. Returns an error after timeout.
func (*FarosClient) WaitForNoPlacement ¶
func (k *FarosClient) WaitForNoPlacement(ctx context.Context, vwName, namespace, edgeName string, timeout time.Duration) error
WaitForNoPlacement polls until no Placement targeting edgeName exists for the given VirtualWorkload — i.e. the scheduler has not routed to that edge. Returns nil when the condition is confirmed within timeout; returns an error if a matching placement still exists at deadline.
func (*FarosClient) WaitForPlacement ¶
func (k *FarosClient) WaitForPlacement(ctx context.Context, vwName, namespace, edgeName string, timeout time.Duration) error
WaitForPlacement polls until a Placement targeting edgeName exists for the given VirtualWorkload or the timeout expires.
type OIDCLoginResult ¶
type OIDCLoginResult struct {
// Kubeconfig is the raw kubeconfig YAML returned by the hub.
Kubeconfig []byte
// Email is the authenticated user's email address.
Email string
// UserID is the authenticated user's ID in the hub.
UserID string
// IDToken is the raw OIDC ID token for direct API calls or caching.
IDToken string
// RefreshToken can be used to refresh the ID token.
RefreshToken string
// ExpiresAt is the Unix timestamp when the ID token expires.
ExpiresAt int64
// IssuerURL is the OIDC issuer URL embedded in the kubeconfig.
IssuerURL string
// ClientID is the OAuth2 client ID.
ClientID string
}
OIDCLoginResult holds the result of a headless OIDC login.
func HeadlessOIDCLogin ¶
func HeadlessOIDCLogin(ctx context.Context, hubURL, email, password string) (*OIDCLoginResult, error)
HeadlessOIDCLogin drives the full OIDC authorization-code flow headlessly.
The faros hub auth flow (see pkg/server/auth/handler.go):
- GET /auth/authorize?p=<port>&s=<session> → 302 to Dex auth URL
- GET Dex auth URL → Dex login page
- POST Dex login form with credentials → 302 to hub /auth/callback
- GET hub /auth/callback?code=…&state=… → 302 to localhost:<port>/callback?response=<b64>
- GET localhost:<port>/callback?response=… → parse LoginResponse JSON
The function starts a local HTTP server on a random port to receive step 5, then drives steps 1–4 using a cookie-aware HTTP client.
type SAResponse ¶
type SSHWebSocketClient ¶
type SSHWebSocketClient struct {
// contains filtered or unexported fields
}
SSHWebSocketClient is a programmatic WebSocket SSH client for use in e2e tests. It connects directly to the hub SSH subresource endpoint, bypassing the CLI, so that the interactive PTY path can be exercised without a real terminal.
A single background reader goroutine pumps all inbound WebSocket frames into a buffered channel. CollectOutput drains that channel with a time.Timer instead of using SetReadDeadline. This avoids the gorilla/websocket behaviour of permanently storing read errors: once a deadline fires, c.readErr is set and every subsequent ReadMessage returns that error immediately, making the connection unusable for further reads.
func DialSSH ¶
func DialSSH(ctx context.Context, kubeconfig, name string) (*SSHWebSocketClient, error)
DialSSH connects to the hub SSH WebSocket endpoint for the given edge name. kubeconfig is used to extract the hub credentials and to look up the edge's status.URL (which contains the correct cluster name — not the static "default" path that was previously hardcoded here).
func (*SSHWebSocketClient) Close ¶
func (c *SSHWebSocketClient) Close() error
Close closes the WebSocket connection.
func (*SSHWebSocketClient) CollectOutput ¶
CollectOutput drains WebSocket messages for up to timeout and returns all output concatenated as a string. It uses a time.Timer (not SetReadDeadline) so that the underlying connection remains usable after the call returns.
func (*SSHWebSocketClient) SendInput ¶
func (c *SSHWebSocketClient) SendInput(data []byte) error
SendInput sends raw bytes as a cmd message (base64-encoded).
func (*SSHWebSocketClient) SendResize ¶
func (c *SSHWebSocketClient) SendResize(cols, rows int) error
SendResize sends a terminal resize message.
type ServerContainer ¶
type ServerContainer struct {
// Name is the Docker container name.
Name string
// ServerName is the faros Server resource name to register on the hub.
ServerName string
// HubURL is the URL of the faros hub, reachable from the runner's network.
HubURL string
// HubCluster is the kcp logical cluster name (e.g. "1tww43gelbj45g0k").
// When set it is passed to the agent via --cluster so that the tunnel is
// registered under the correct key. Obtain it with ClusterNameFromKubeconfig.
HubCluster string
// Token is the bearer token for the agent.
Token string
// AgentBin is the host path to the faros binary.
AgentBin string
}
ServerContainer manages a Docker container running lscr.io/linuxserver/openssh-server alongside a faros server-mode agent. The container runs with --network host so the agent can reach the hub at faros.localhost:9443.
func ServerContainerFromContext ¶
func ServerContainerFromContext(ctx context.Context) (*ServerContainer, bool)
ServerContainerFromContext retrieves a ServerContainer from the context.
func (*ServerContainer) AgentLogs ¶
func (s *ServerContainer) AgentLogs(ctx context.Context) (string, error)
AgentLogs returns the agent log from inside the container.
func (*ServerContainer) Start ¶
func (s *ServerContainer) Start(ctx context.Context) error
Start launches the container, waits for sshd, copies the agent, and starts it.
func (*ServerContainer) Stop ¶
func (s *ServerContainer) Stop(ctx context.Context) error
Stop removes the container.
func (*ServerContainer) WaitForAgentReady ¶
WaitForAgentReady polls until the agent log shows the tunnel is connected.
type ServerProcess ¶
type ServerProcess struct {
// ServerName is the faros Server resource name to register on the hub.
ServerName string
// HubURL is the URL of the faros hub (base URL, no /clusters/ path).
// Used only when HubKubeconfig is empty.
HubURL string
// HubKubeconfig is the path to a kubeconfig whose server URL contains the
// kcp workspace cluster path (e.g. https://hub:9443/clusters/abc123).
// When set the agent uses --hub-kubeconfig instead of --hub-url so that the
// cluster name is correctly derived from the URL. Always set this in e2e
// tests to avoid the cluster-name mismatch bug with static tokens.
HubKubeconfig string
// Token is the bearer token for the agent.
Token string
// AgentBin is the path to the faros binary.
AgentBin string
// SSHPort is the port for the embedded test SSH server. Defaults to
// DefaultTestSSHPort if zero.
SSHPort int
// SSHUser is the SSH username reported to the hub in Edge.Status.SSHCredentials.
// When set alongside SSHPassword, the agent will register credentials so that
// SSHUserMappingInherited tests can verify the username.
// +optional
SSHUser string
// SSHPassword is the SSH password reported to the hub.
// Required alongside SSHUser to trigger credential registration.
// +optional
SSHPassword string
// SSHServer may be pre-populated with an already-configured TestSSHServer
// (e.g. one set up with AddUser / AddAnyUserKey). If non-nil it will be
// used directly and its Start() method will be called; the SSHPort field
// must match the server's Port.
// If nil, a new TestSSHServer bound to SSHPort is created automatically.
// After Start() returns, SSHServer is always set (whether pre-configured or
// freshly created) and can be used to inspect ConnectedUsers.
SSHServer *TestSSHServer
// contains filtered or unexported fields
}
ServerProcess runs a faros server-mode agent as a local subprocess together with an embedded test SSH server. This replaces the Docker-based ServerContainer and avoids all external SSH daemon configuration issues.
func ServerProcessFromContext ¶
func ServerProcessFromContext(ctx context.Context) (*ServerProcess, bool)
ServerProcessFromContext retrieves a ServerProcess from the context.
func (*ServerProcess) Logs ¶
func (s *ServerProcess) Logs() string
Logs returns the combined stdout+stderr of the agent subprocess.
func (*ServerProcess) Start ¶
func (s *ServerProcess) Start(ctx context.Context) error
Start launches the embedded SSH server and the agent subprocess.
func (*ServerProcess) Stop ¶
func (s *ServerProcess) Stop()
Stop kills the agent subprocess and shuts down the SSH server.
func (*ServerProcess) WaitForAgentReady ¶
WaitForAgentReady polls until the agent has started AND the revdial tunnel is connected (i.e. the hub can reach back to this agent for SSH sessions).
type TestSSHServer ¶
type TestSSHServer struct {
Port int
// contains filtered or unexported fields
}
TestSSHServer is a minimal embedded SSH server for e2e tests. It accepts any authentication by default (security is provided by the revdial tunnel that already authenticated the caller) and executes commands via exec.Command. It is not safe for production use.
To restrict authentication to specific users/keys, call AddUser or AddAnyUserKey before Start.
func NewTestSSHServer ¶
func NewTestSSHServer(port int) *TestSSHServer
NewTestSSHServer creates a TestSSHServer bound to the given port.
func (*TestSSHServer) AddAnyUserKey ¶
func (s *TestSSHServer) AddAnyUserKey(pubKey gossh.PublicKey)
AddAnyUserKey configures the server to accept the given public key for any username. Useful for SSHUserMappingIdentity tests where the username is determined at runtime.
func (*TestSSHServer) AddUser ¶
func (s *TestSSHServer) AddUser(username string, pubKey gossh.PublicKey)
AddUser configures the server to accept the given public key for the given username. May be called multiple times to add multiple users or multiple keys per user. When at least one call to AddUser or AddAnyUserKey has been made, NoClientAuth is disabled and only the configured credentials are accepted.
func (*TestSSHServer) ConnectedUsers ¶
func (s *TestSSHServer) ConnectedUsers() []string
ConnectedUsers returns a snapshot of usernames that have authenticated since the server started. Safe to call concurrently with Start.
type TokenAgent ¶
type TokenAgent struct {
// contains filtered or unexported fields
}
TokenAgent manages a faros agent run process authenticated via a bootstrap join token instead of a SA-backed hub kubeconfig.
func NewAgentWithToken ¶
func NewAgentWithToken(workDir, hubURL, edgeName, token string) *TokenAgent
NewAgentWithToken creates a TokenAgent that connects to the hub using a bootstrap join token (faros agent run --token). Use agentKubeconfig="" for server-type edges that have no downstream Kubernetes cluster.
func NewReconnectAgent ¶
func NewReconnectAgent(workDir, hubURL, edgeName string) *TokenAgent
NewReconnectAgent creates a TokenAgent that reconnects to the hub using the previously saved kubeconfig (written during the first successful join-token exchange). No --token is passed — the binary's built-in auto-detection reads the saved kubeconfig from ~/.faros/agent-<edgeName>.kubeconfig.
Use this to verify the reconnect-after-restart flow end-to-end.
func (*TokenAgent) Start ¶
func (a *TokenAgent) Start(ctx context.Context) error
Start launches the faros agent run process with the configured join token. It runs until Stop is called or the parent context is cancelled. When token is empty (e.g. NewReconnectAgent), no --token flag is passed and the binary auto-discovers the saved kubeconfig from ~/.faros/.
func (*TokenAgent) WithAgentKubeconfig ¶
func (a *TokenAgent) WithAgentKubeconfig(kc string) *TokenAgent
WithAgentKubeconfig sets the kubeconfig for the downstream Kubernetes cluster. For server-type edges this is not required.
func (*TokenAgent) WithCluster ¶
func (a *TokenAgent) WithCluster(clusterName string) *TokenAgent
WithCluster sets the kcp logical cluster name so the agent connects to the correct workspace on the hub. Required when using a join token because the token alone does not carry cluster information.
func (*TokenAgent) WithSSHPassword ¶
func (a *TokenAgent) WithSSHPassword(pass string) *TokenAgent
WithSSHPassword sets the SSH password the agent reports to the hub via X-Faros-SSH-Password WebSocket header (join-token mode) or the --ssh-password flag.
func (*TokenAgent) WithSSHUser ¶
func (a *TokenAgent) WithSSHUser(user string) *TokenAgent
WithSSHUser sets the SSH username the agent reports to the hub via X-Faros-SSH-User WebSocket header (join-token mode) or the --ssh-user flag.
func (*TokenAgent) WithType ¶
func (a *TokenAgent) WithType(t string) *TokenAgent
WithType overrides the edge type (default "server").