Compare commits

...

14 Commits

Author SHA1 Message Date
Volodymyr Zotov
5ccb0d88bb Merge pull request #224 from 1Password/vzt/test-contributions-workflow
Add workflow to test contributors' PRs
2025-10-02 08:16:43 -05:00
Volodymyr Zotov
3f52bb2840 Add new line at the end of the file 2025-10-02 08:09:04 -05:00
Volodymyr Zotov
7650aef60a Merge pull request #225 from 1Password/vzt/testhelper-module
Introduce testhelper module
2025-10-02 08:07:26 -05:00
Volodymyr Zotov
63e3f29be9 Refactor e2e test workflows 2025-09-30 21:44:16 -05:00
Volodymyr Zotov
49bc9cb329 Use workflow anchors 2025-09-30 21:37:11 -05:00
Volodymyr Zotov
a5e4a352e9 Update readme 2025-09-30 16:59:30 -05:00
Volodymyr Zotov
edde903759 Do not use first pod, but look for matching pod in array 2025-09-30 16:56:10 -05:00
Volodymyr Zotov
de62e07bcf Bump action versions in other workflows 2025-09-11 12:20:40 -05:00
Volodymyr Zotov
3ebc536dd7 Add empty line at the end of the file 2025-09-11 11:50:02 -05:00
Volodymyr Zotov
6769e25a98 Do not run e2e tests when making a change on documentation or not realted to the operator files 2025-09-11 11:49:09 -05:00
Volodymyr Zotov
d8734c9ae3 Run e2e tests when pusing to main and bump actions to the latest 2025-09-11 11:16:47 -05:00
Volodymyr Zotov
460742869b Add workflow to run e2e tests on contributor's branch 2025-09-11 11:14:10 -05:00
Volodymyr Zotov
35e476230c Add ok-to-test workflow 2025-09-11 10:52:53 -05:00
Volodymyr Zotov
3a9691576a Add ok to test workflow 2025-09-05 13:45:29 -05:00
11 changed files with 196 additions and 57 deletions

View File

@@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version-file: go.mod

52
.github/workflows/e2e-tests.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: E2E Tests
on:
workflow_call:
secrets:
OP_CONNECT_CREDENTIALS:
description: '1Password Connect credentials'
required: true
OP_CONNECT_TOKEN:
description: '1Password Connect token'
required: true
OP_SERVICE_ACCOUNT_TOKEN:
description: '1Password service account token'
required: true
jobs:
e2e-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Install dependencies
run: go mod tidy
- name: Create kind cluster
uses: helm/kind-action@v1
with:
cluster_name: onepassword-operator-test-e2e
# install cli to interact with item in 1Password to update/read using `testhelper/op` package
- name: Install 1Password CLI
uses: 1password/install-cli-action@v2
with:
version: 2.32.0
- name: Create '1password-credentials.json' file
env:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
run: |
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
- name: Run E2E tests
run: make test-e2e
env:
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}

View File

@@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version-file: go.mod

25
.github/workflows/ok-to-test.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# Write comments "/ok-to-test <hash>" on a pull request. This will emit a repository_dispatch event.
name: Ok To Test
on:
issue_comment:
types: [created]
jobs:
ok-to-test:
runs-on: ubuntu-latest
permissions:
pull-requests: write # For adding reactions to the pull request comments
contents: write # For executing the repository_dispatch event
# Only run for PRs, not issue comments
if: ${{ github.event.issue.pull_request }}
steps:
- name: Slash Command Dispatch
uses: volodymyrZotov/slash-command-dispatch@7c1b623a2b0eba93f684c34f689a441f0be84cf1 # TODO: use peter-evans/slash-command-dispatch when fix for team permissions is released https://github.com/peter-evans/slash-command-dispatch/pull/424
with:
token: ${{ secrets.GITHUB_TOKEN }}
reaction-token: ${{ secrets.GITHUB_TOKEN }}
issue-type: pull-request
commands: ok-to-test
# The repository permission level required by the user to dispatch commands. Only allows 1Password collaborators to run this.
permission: write

View File

@@ -43,7 +43,7 @@ jobs:
name: Create Release Pull Request
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Parse release version
id: get_version

View File

@@ -12,7 +12,7 @@ jobs:
DOCKER_CLI_EXPERIMENTAL: "enabled"
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0

56
.github/workflows/test-e2e-fork.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: E2E tests [fork]
on:
repository_dispatch:
types: [ ok-to-test-command ]
permissions:
contents: read
checks: write
concurrency:
group: e2e-fork-${{ github.event.client_payload.pull_request.number || github.run_id }}
cancel-in-progress: true # cancel previous job runs for the same branch
jobs:
e2e-tests:
uses: ./.github/workflows/e2e-tests.yml
if: |
github.event_name == 'repository_dispatch' &&
github.event.client_payload.slash_command.args.named.sha != '' &&
contains(
github.event.client_payload.pull_request.head.sha,
github.event.client_payload.slash_command.args.named.sha
)
secrets:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
update-check-status:
needs: e2e-tests
runs-on: ubuntu-latest
if: always() && github.event_name == 'repository_dispatch'
steps:
- uses: actions/github-script@v6
env:
ref: ${{ github.event.client_payload.pull_request.head.sha }}
conclusion: ${{ needs.e2e-tests.result }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: checks } = await github.rest.checks.listForRef({
...context.repo,
ref: process.env.ref
});
const check = checks.check_runs.filter(c => c.name === 'e2e-test');
const { data: result } = await github.rest.checks.update({
...context.repo,
check_run_id: check[0].id,
status: 'completed',
conclusion: process.env.conclusion
});
return result;

View File

@@ -1,48 +1,43 @@
name: Test E2E
name: E2E Tests
on:
pull_request:
types: [opened, synchronize, reopened]
branches: ['**'] # run for PRs targeting any branch (main and others)
paths-ignore: &ignore_paths
- 'docs/**'
- '*.md'
- '.golangci.yml'
- '.gitignore'
- '.dockerignore'
- 'LICENSE'
push:
branches: [main]
paths-ignore: *ignore_paths
concurrency:
group: e2e-${{ github.event.pull_request.head.ref }}
cancel-in-progress: true # cancel previous job runs for the same branch
cancel-in-progress: true # cancel previous job runs for the same branch
jobs:
e2e-test:
check-external-pr:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Install dependencies
run: go mod tidy
- name: Create kind cluster
uses: helm/kind-action@v1
with:
cluster_name: onepassword-operator-test-e2e
# install cli to interact with item in 1Password to update/read using `testhelper/op` package
- name: Install 1Password CLI
uses: 1password/install-cli-action@v2
with:
version: 2.32.0
- name: Create '1password-credentials.json' file
env:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
- name: Check if PR is from external contributor
run: |
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
echo "❌ External PR detected. This workflow requires approval from a maintainer."
echo "Please ask a maintainer to run '/ok-to-test' command to trigger the fork workflow."
exit 1
fi
echo "✅ Internal PR detected. Proceeding with tests."
- name: Run E2E tests
run: make test-e2e
env:
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
e2e-test:
needs: check-external-pr
if: always() && (needs.check-external-pr.result == 'success' || github.event_name != 'pull_request')
uses: ./.github/workflows/e2e-tests.yml
secrets:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}

View File

@@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version-file: go.mod

View File

@@ -1,6 +1,6 @@
# OnePassword Operator Test Helper
This is a standalone Go module that provides testing utilities for Kubernetes operators, specifically designed for testing OnePassword operators but can be used for any Kubernetes operator testing.
This is a standalone Go module that provides testing utilities for Kubernetes operators and webhooks. It's specifically designed for testing 1Password Kubernetes operator and secrets injector, but it can be used for any Kubernetes operator or webhook testing.
## Installation
@@ -35,19 +35,19 @@ kubeClient := kube.NewKubeClient(&kube.Config{
### Working with Secrets
```go
// Create a secret from environment variable
secret := kubeClient.Secret("my-secret")
secret.CreateFromEnvVar(ctx, "MY_ENV_VAR")
// Create k8s secret from environment variable
k8sSecret := kubeClient.Secret("my-secret")
k8sSecret.CreateFromEnvVar(ctx, "MY_ENV_VAR")
// Create a secret from file
data := []byte("secret content")
secret.CreateFromFile(ctx, "filename", data)
k8sSecret.CreateFromFile(ctx, "filename", data)
// Check if secret exists
secret.CheckIfExists(ctx)
k8sSecret.CheckIfExists(ctx)
// Get secret
secretObj := secret.Get(ctx)
secretObj := k8sSecret.Get(ctx)
```
### Working with Deployments

View File

@@ -60,13 +60,17 @@ func (p *Pod) GetPodLogs(ctx context.Context) string {
Expect(err).NotTo(HaveOccurred())
Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
// Use the first pod found
pod := pods.Items[0]
// Find a running pod to get logs from
var pod *corev1.Pod
for i := range pods.Items {
if pods.Items[i].Status.Phase == corev1.PodRunning {
pod = &pods.Items[i]
break
}
}
Expect(pod).NotTo(BeNil(), "no running pod found with selector %q", labels.Set(p.selector).String())
podName := pod.Name
// Verify pod is running before getting logs
Expect(pod.Status.Phase).To(Equal(corev1.PodRunning), "pod %s is not running (status: %s)", podName, pod.Status.Phase)
// Get logs using the Kubernetes clientset
req := p.clientset.CoreV1().Pods(p.config.Namespace).GetLogs(podName, &corev1.PodLogOptions{})
stream, err := req.Stream(context.TODO())
@@ -97,8 +101,15 @@ func (p *Pod) VerifyWebhookInjection(ctx context.Context) {
g.Expect(p.client.List(attemptCtx, &pods, listOpts...)).To(Succeed())
g.Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
// Use the first pod found
pod := pods.Items[0]
// Find a running pod to verify webhook injection
var pod *corev1.Pod
for i := range pods.Items {
if pods.Items[i].Status.Phase == corev1.PodRunning {
pod = &pods.Items[i]
break
}
}
g.Expect(pod).NotTo(BeNil(), "no running pod found with selector %q", labels.Set(p.selector).String())
// Check injection status annotation
g.Expect(pod.Annotations).To(HaveKey("operator.1password.io/status"))