Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ End-to-end test suite for TSF (Trusted Software Factory) instances.
## Prerequisites

- **KUBECONFIG** pointing to a cluster with TSF installed
- **GitHub org** with a fork/clone of [konflux-ci/testrepo](https://github.com/konflux-ci/testrepo)
- **GitHub token** (PAT) with repo access to the org above
- **Git hosting** for the component under test (set **`GIT_PROVIDER`** to **`gitlab`** or **`gl`** for GitLab; use **`github`** or leave unset for GitHub — unknown values **fail fast** so you do not silently run the GitHub path):
- **GitHub** (default): org with a fork or clone of [konflux-ci/testrepo](https://github.com/konflux-ci/testrepo), or your own repo; set `MY_GITHUB_ORG` and optionally `MY_GITHUB_REPO` (defaults to `testrepo`).
- **GitLab**: set `GIT_PROVIDER=gitlab`, `MY_GITLAB_PROJECT_PATH` (URL path only, e.g. `group/subgroup/repo` on GitLab.com, not a numeric project id), and `GITLAB_BOT_TOKEN` or `GITLAB_TOKEN`. Optional `GITLAB_DEFAULT_BRANCH` defaults to `main`. **`GITLAB_SOURCE_REVISION` is optional**: if unset, the test creates the base branch from the **latest commit on the default branch** (use this when your GitLab copy does not contain the same commit SHA as the GitHub `GITHUB_SOURCE_REVISION` default). If set, it must be a commit that exists in the GitLab project. Optional `GITLAB_BASE_URL` defaults to `https://gitlab.com`; if `GITLAB_API_URL` is unset, the test derives `GITLAB_BASE_URL/api/v4` before the framework starts (override `GITLAB_API_URL` if your instance needs a different API base).
- **Token** with permission to manage the chosen repo (GitHub: `GITHUB_TOKEN`; GitLab: see above). For **GitLab PaC**, the test creates **`pipelines-as-code-secret`** ( **`password`** + **`provider.token`** only) in the applications namespace when absent; after the **component** exists, Konflux writes **`pipelines-as-code-webhooks-secret`**, and the test copies **`webhook.secret`** from the data key **`KonfluxPACWebhookSecretDataKey(GitSource.URL)`** (same rule as konflux build-service, e.g. `https___gitlab.com_group_repo` for `https://gitlab.com/group/repo.git`). The GitLab project hook token is **only** that value—no fallbacks. Ensure `GIT_PROVIDER=gitlab` is set—timeouts mentioning only short repo name `testrepo` usually mean the **GitHub** code path (default provider), not GitLab.
- **Quay org** accessible by the TSF instance's image-controller

## IDE Setup
Expand Down
6 changes: 3 additions & 3 deletions e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ go 1.25.0

require (
github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd
github.com/google/go-github/v44 v44.1.0
github.com/konflux-ci/application-api v0.0.0-20251210122215-555a927cf6d9
github.com/konflux-ci/build-service v0.0.0-20240611083846-2dee6cfe6fe4
github.com/konflux-ci/e2e-tests v0.0.0-00010101000000-000000000000
github.com/konflux-ci/image-controller v0.0.0-20240530145826-3296e4996f6f
github.com/konflux-ci/integration-service v0.0.0-20260108133201-e2f5559a9544
github.com/konflux-ci/release-service v0.0.0-20260113075649-fff62d349fa9
github.com/onsi/ginkgo/v2 v2.27.4
github.com/onsi/gomega v1.39.0
github.com/tektoncd/pipeline v1.9.2
github.com/xanzy/go-gitlab v0.114.0
k8s.io/api v0.35.1
k8s.io/apimachinery v0.35.1
k8s.io/klog/v2 v2.130.1
Expand Down Expand Up @@ -106,6 +105,7 @@ require (
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.7 // indirect
github.com/google/go-github/v44 v44.1.0 // indirect
github.com/google/go-github/v45 v45.2.0 // indirect
github.com/google/go-github/v75 v75.0.0 // indirect
github.com/google/go-querystring v1.2.0 // indirect
Expand All @@ -127,6 +127,7 @@ require (
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/compress v1.18.1 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/konflux-ci/image-controller v0.0.0-20240530145826-3296e4996f6f // indirect
github.com/konflux-ci/operator-toolkit v0.0.0-20251118152634-b4f41f073069 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/magefile/mage v1.14.0 // indirect
Expand Down Expand Up @@ -179,7 +180,6 @@ require (
github.com/tektoncd/cli v0.43.0 // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/go-gitlab v0.114.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opencensus.io v0.24.0 // indirect
Expand Down
27 changes: 26 additions & 1 deletion e2e/my-test.env.template
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
# Required
export KUBECONFIG=~/.kube/config
export E2E_APPLICATIONS_NAMESPACE=default-tenant # Use a specific namespace instead of generating one

# Git provider: omit or "github" for GitHub; "gitlab" or "gl" for GitLab (other values error out)
# export GIT_PROVIDER=github

# --- GitHub (when GIT_PROVIDER is unset or github) ---
export GITHUB_TOKEN=
export MY_GITHUB_ORG=
export E2E_APPLICATIONS_NAMESPACE=default-tenant # Use a specific namespace instead of generating one
export MY_GITHUB_REPO=testrepo
# export GITHUB_SOURCE_REVISION=1255dc36534b9db7b99efbc281117435ea03255f

# Names must match setup-release.sh (-a / -c / -m); release flow expects these to exist
export TSF_APPLICATION_NAME=tsf-demo-app # Must match what setup-release.sh was called with
export TSF_COMPONENT_NAME=tsf-demo-comp # Must match what setup-release.sh was called with
export TSF_MANAGED_NAMESPACE=default-managed-tenant # Must match what setup-release.sh was called with

# --- GitLab (when GIT_PROVIDER=gitlab) ---
# Requires: export GIT_PROVIDER=gitlab
# The test creates pipelines-as-code-secret in your tenant namespace if missing (PaC needs it to push konflux-* branches).
# export GITLAB_BASE_URL=https://gitlab.com
# export MY_GITLAB_PROJECT_PATH=my-group/my-testrepo
# export GITLAB_DEFAULT_BRANCH=main
# Optional: commit SHA to branch from. Leave unset to use the latest commit on GITLAB_DEFAULT_BRANCH
# (recommended if your GitLab repo does not contain the GitHub GITHUB_SOURCE_REVISION SHA).
# export GITLAB_SOURCE_REVISION=
# Token: konflux e2e-tests reads GITLAB_BOT_TOKEN; optional alias:
# export GITLAB_BOT_TOKEN=
# export GITLAB_TOKEN=
# After the component is created, webhook material is read only from pipelines-as-code-webhooks-secret
# (tenant namespace), using the Konflux data key derived from the component Git clone URL.
# If GITLAB_API_URL is unset, it defaults to ${GITLAB_BASE_URL}/api/v4 (set explicitly for unusual layouts)

# Optional
# export E2E_SKIP_CLEANUP=true # Keep test resources after run (useful for debugging)
# export SKIP_PAC_TESTS=true # Skip the entire test
Expand Down
150 changes: 150 additions & 0 deletions e2e/tests/tsf/git-providers/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package gitproviders

import (
"fmt"
"net/url"
"os"
"strings"

"github.com/konflux-ci/e2e-tests/pkg/constants"
)

// Environment variable names for TSF demo e2e (in addition to konflux e2e-tests vars).
const (
EnvGitProvider = "GIT_PROVIDER"
EnvMyGithubRepo = "MY_GITHUB_REPO"
EnvGithubSourceRevision = "GITHUB_SOURCE_REVISION"
EnvGitlabBaseURL = "GITLAB_BASE_URL"
EnvMyGitlabProjectPath = "MY_GITLAB_PROJECT_PATH"
EnvGitlabSourceRevision = "GITLAB_SOURCE_REVISION"
EnvGitlabDefaultBranch = "GITLAB_DEFAULT_BRANCH"
EnvGitlabTokenAlias = "GITLAB_TOKEN" // copied to GITLAB_BOT_TOKEN before framework init if needed
defaultGithubRepo = "testrepo"
defaultGitlabBaseURL = "https://gitlab.com"
defaultGitlabBranch = "main"
defaultGithubSrcRevision = "1255dc36534b9db7b99efbc281117435ea03255f"
)

// Kind selects which SCM API the demo test uses.
type Kind string

const (
KindGitHub Kind = "github"
KindGitLab Kind = "gitlab"
)

// ParseGitProvider returns KindGitHub when raw is empty.
// Accepted non-empty values: github, gitlab, gl (case-insensitive, UTF-8 BOM trimmed).
// Any other value returns an error so typos do not silently use GitHub while you expect GitLab.
func ParseGitProvider(raw string) (Kind, error) {
s := strings.TrimPrefix(strings.TrimSpace(raw), "\ufeff")
if s == "" {
return KindGitHub, nil
}
switch strings.ToLower(s) {
case string(KindGitHub):
return KindGitHub, nil
case string(KindGitLab), "gl":
return KindGitLab, nil
default:
return KindGitHub, fmt.Errorf("%s=%q is invalid: use github, gitlab, or gl (omit for github)", EnvGitProvider, raw)
}
}

// PrepareProcessEnvForGitLab sets konflux framework variables from TSF-friendly
// aliases before framework.NewFramework. Call only when Kind == KindGitLab.
func PrepareProcessEnvForGitLab() {
if os.Getenv(constants.GITLAB_BOT_TOKEN_ENV) == "" {
if t := strings.TrimSpace(os.Getenv(EnvGitlabTokenAlias)); t != "" {
_ = os.Setenv(constants.GITLAB_BOT_TOKEN_ENV, t)
}
}
if strings.TrimSpace(os.Getenv(constants.GITLAB_API_URL_ENV)) == "" {
base := normalizedGitlabOriginURL(os.Getenv(EnvGitlabBaseURL))
_ = os.Setenv(constants.GITLAB_API_URL_ENV, strings.TrimSuffix(base, "/")+"/api/v4")
}
}

// normalizedGitlabOriginURL returns a URL with scheme suitable for joining paths (e.g. https://gitlab.com).
func normalizedGitlabOriginURL(raw string) string {
base := strings.TrimSuffix(strings.TrimSpace(raw), "/")
if base == "" {
base = defaultGitlabBaseURL
}
if !strings.HasPrefix(base, "http://") && !strings.HasPrefix(base, "https://") {
base = "https://" + base
}
return base
}

// GitlabHostForSCMLabel returns the hostname from GITLAB_BASE_URL for appstudio.redhat.com/scm.host (must match clone URL host).
func GitlabHostForSCMLabel() (string, error) {
u, err := url.Parse(normalizedGitlabOriginURL(os.Getenv(EnvGitlabBaseURL)))
if err != nil {
return "", fmt.Errorf("parse GITLAB_BASE_URL: %w", err)
}
h := u.Hostname()
if h == "" {
return "", fmt.Errorf("GITLAB_BASE_URL has no host (set e.g. https://gitlab.com)")
}
return h, nil
}

// GithubComponentURL returns https clone URL for org/repo.
func GithubComponentURL(org, repo string) string {
org = strings.TrimSpace(org)
repo = strings.TrimSpace(repo)
return fmt.Sprintf("https://github.com/%s/%s", org, repo)
}

// GitlabComponentURL returns https clone URL for a project path on a GitLab host.
func GitlabComponentURL(baseURL, projectPath string) string {
baseURL = strings.TrimSuffix(normalizedGitlabOriginURL(baseURL), "/")
projectPath = strings.Trim(strings.TrimSpace(projectPath), "/")
return fmt.Sprintf("%s/%s.git", baseURL, projectPath)
}

// RequiredGithubRepo returns MY_GITHUB_REPO or default testrepo.
func RequiredGithubRepo() string {
if r := strings.TrimSpace(os.Getenv(EnvMyGithubRepo)); r != "" {
return r
}
return defaultGithubRepo
}

// GithubSourceRevision returns GITHUB_SOURCE_REVISION or the historical default SHA.
func GithubSourceRevision() string {
if r := strings.TrimSpace(os.Getenv(EnvGithubSourceRevision)); r != "" {
return r
}
return defaultGithubSrcRevision
}

// GitlabBaseURL returns GITLAB_BASE_URL or https://gitlab.com (trimmed, no trailing slash).
func GitlabBaseURL() string {
return strings.TrimSuffix(normalizedGitlabOriginURL(os.Getenv(EnvGitlabBaseURL)), "/")
}

// RequiredGitlabProjectPath returns MY_GITLAB_PROJECT_PATH (e.g. group/sub/repo).
func RequiredGitlabProjectPath() (string, error) {
p := strings.Trim(strings.TrimSpace(os.Getenv(EnvMyGitlabProjectPath)), "/")
if p == "" {
return "", fmt.Errorf("%s is not set", EnvMyGitlabProjectPath)
}
return p, nil
}

// GitlabSourceRevision returns GITLAB_SOURCE_REVISION, or empty to mean "tip of the default branch".
// Empty is recommended when the GitHub default SHA (GITHUB_SOURCE_REVISION) is not present in your
// GitLab project; konflux CreateGitlabNewBranch then resolves the latest commit on the default branch.
func GitlabSourceRevision() string {
return strings.TrimSpace(os.Getenv(EnvGitlabSourceRevision))
}

// GitlabDefaultBranch returns GITLAB_DEFAULT_BRANCH or "main".
func GitlabDefaultBranch() string {
if b := strings.TrimSpace(os.Getenv(EnvGitlabDefaultBranch)); b != "" {
return b
}
return defaultGitlabBranch
}
54 changes: 54 additions & 0 deletions e2e/tests/tsf/git-providers/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package gitproviders

import (
"fmt"
"strings"

"github.com/konflux-ci/e2e-tests/pkg/framework"
"github.com/konflux-ci/e2e-tests/pkg/utils/build"
)

type githubProvider struct {
fw *framework.Framework
repoName string
}

func (g *githubProvider) CreateBaseBranch(defaultBranch, gitRevision, newBranch string) error {
return g.fw.AsKubeAdmin.CommonController.Github.CreateRef(g.repoName, defaultBranch, gitRevision, newBranch)
}

func (g *githubProvider) WaitPaCInitChange(pacBranch string) (int, string, error) {
prs, err := g.fw.AsKubeAdmin.CommonController.Github.ListPullRequests(g.repoName)
if err != nil {
return 0, "", err
}
for _, pr := range prs {
if pr.Head.GetRef() == pacBranch {
return pr.GetNumber(), pr.GetHead().GetSHA(), nil
}
}
return 0, "", fmt.Errorf("could not get the expected PaC branch name %s", pacBranch)
}

func (g *githubProvider) MergePaCChange(changeID int) (string, error) {
res, err := g.fw.AsKubeAdmin.CommonController.Github.MergePullRequest(g.repoName, changeID)
if err != nil {
return "", err
}
return res.GetSHA(), nil
}

func (g *githubProvider) DeleteRemoteBranch(branch string) error {
err := g.fw.AsKubeAdmin.CommonController.Github.DeleteRef(g.repoName, branch)
if err == nil {
return nil
}
if strings.Contains(err.Error(), "Reference does not exist") {
return nil
}
return err
}

func (g *githubProvider) CleanupClusterWebhooks() error {
return build.CleanupWebhooks(g.fw, g.repoName)
}
71 changes: 71 additions & 0 deletions e2e/tests/tsf/git-providers/gitlab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package gitproviders

import (
"fmt"
"strings"

"github.com/konflux-ci/e2e-tests/pkg/framework"
)

type gitlabProvider struct {
fw *framework.Framework
projectPath string
}

func (g *gitlabProvider) CreateBaseBranch(defaultBranch, gitRevision, newBranch string) error {
return g.fw.AsKubeAdmin.CommonController.Gitlab.CreateGitlabNewBranch(g.projectPath, newBranch, gitRevision, defaultBranch)
}

func (g *gitlabProvider) WaitPaCInitChange(pacBranch string) (int, string, error) {
mrs, err := g.fw.AsKubeAdmin.CommonController.Gitlab.GetMergeRequests(g.projectPath)
if err != nil {
return 0, "", err
}
for _, mr := range mrs {
if mr.SourceBranch == pacBranch {
return mr.IID, mr.SHA, nil
}
}
branches := make([]string, 0, len(mrs))
for _, mr := range mrs {
branches = append(branches, mr.SourceBranch)
}
return 0, "", fmt.Errorf("no open MR with source branch %q (found %d open MR(s), source branches: %v); "+
"if this stays empty, PaC is not pushing to GitLab—check pipelines-as-code-secret, token scopes, and GitLab integration",
pacBranch, len(mrs), branches)
}

func (g *gitlabProvider) MergePaCChange(changeID int) (string, error) {
mr, err := g.fw.AsKubeAdmin.CommonController.Gitlab.AcceptMergeRequest(g.projectPath, changeID)
if err != nil {
return "", err
}
if mr == nil {
return "", fmt.Errorf("AcceptMergeRequest returned nil merge request")
}
// PaC labels push build PipelineRuns with the merge commit on the target branch (same as GitHub merge API SHA).
// mr.SHA is the MR source-branch / diff HEAD and matches the init on-pull-request run—do not use it here.
if mr.MergeCommitSHA != "" {
return mr.MergeCommitSHA, nil
}
if mr.SquashCommitSHA != "" {
return mr.SquashCommitSHA, nil
}
return mr.SHA, nil
}

func (g *gitlabProvider) DeleteRemoteBranch(branch string) error {
err := g.fw.AsKubeAdmin.CommonController.Gitlab.DeleteBranch(g.projectPath, branch)
if err == nil {
return nil
}
msg := strings.ToLower(err.Error())
if strings.Contains(msg, "404") || strings.Contains(msg, "not found") {
return nil
}
return err
}

func (g *gitlabProvider) CleanupClusterWebhooks() error {
return g.fw.AsKubeAdmin.CommonController.Gitlab.DeleteWebhooks(g.projectPath, g.fw.ClusterAppDomain)
}
Loading