mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-22 07:28:06 +00:00
Compare commits
14 Commits
03b093ac17
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5ccb0d88bb | ||
![]() |
3f52bb2840 | ||
![]() |
7650aef60a | ||
![]() |
63e3f29be9 | ||
![]() |
49bc9cb329 | ||
![]() |
a5e4a352e9 | ||
![]() |
edde903759 | ||
![]() |
de62e07bcf | ||
![]() |
3ebc536dd7 | ||
![]() |
6769e25a98 | ||
![]() |
d8734c9ae3 | ||
![]() |
460742869b | ||
![]() |
35e476230c | ||
![]() |
3a9691576a |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone the code
|
- name: Clone the code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
52
.github/workflows/e2e-tests.yml
vendored
Normal file
52
.github/workflows/e2e-tests.yml
vendored
Normal 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 }}
|
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone the code
|
- name: Clone the code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
25
.github/workflows/ok-to-test.yml
vendored
Normal file
25
.github/workflows/ok-to-test.yml
vendored
Normal 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
|
2
.github/workflows/release-pr.yml
vendored
2
.github/workflows/release-pr.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
|||||||
name: Create Release Pull Request
|
name: Create Release Pull Request
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Parse release version
|
- name: Parse release version
|
||||||
id: get_version
|
id: get_version
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
DOCKER_CLI_EXPERIMENTAL: "enabled"
|
DOCKER_CLI_EXPERIMENTAL: "enabled"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
56
.github/workflows/test-e2e-fork.yml
vendored
Normal file
56
.github/workflows/test-e2e-fork.yml
vendored
Normal 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;
|
63
.github/workflows/test-e2e.yml
vendored
63
.github/workflows/test-e2e.yml
vendored
@@ -1,48 +1,43 @@
|
|||||||
name: Test E2E
|
name: E2E Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
branches: ['**'] # run for PRs targeting any branch (main and others)
|
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:
|
concurrency:
|
||||||
group: e2e-${{ github.event.pull_request.head.ref }}
|
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:
|
jobs:
|
||||||
e2e-test:
|
check-external-pr:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Check if PR is from external contributor
|
||||||
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 }}
|
|
||||||
run: |
|
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
|
e2e-test:
|
||||||
run: make test-e2e
|
needs: check-external-pr
|
||||||
env:
|
if: always() && (needs.check-external-pr.result == 'success' || github.event_name != 'pull_request')
|
||||||
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
uses: ./.github/workflows/e2e-tests.yml
|
||||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
secrets:
|
||||||
|
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
||||||
|
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
||||||
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone the code
|
- name: Clone the code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# OnePassword Operator Test Helper
|
# 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
|
## Installation
|
||||||
|
|
||||||
@@ -35,19 +35,19 @@ kubeClient := kube.NewKubeClient(&kube.Config{
|
|||||||
### Working with Secrets
|
### Working with Secrets
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Create a secret from environment variable
|
// Create k8s secret from environment variable
|
||||||
secret := kubeClient.Secret("my-secret")
|
k8sSecret := kubeClient.Secret("my-secret")
|
||||||
secret.CreateFromEnvVar(ctx, "MY_ENV_VAR")
|
k8sSecret.CreateFromEnvVar(ctx, "MY_ENV_VAR")
|
||||||
|
|
||||||
// Create a secret from file
|
// Create a secret from file
|
||||||
data := []byte("secret content")
|
data := []byte("secret content")
|
||||||
secret.CreateFromFile(ctx, "filename", data)
|
k8sSecret.CreateFromFile(ctx, "filename", data)
|
||||||
|
|
||||||
// Check if secret exists
|
// Check if secret exists
|
||||||
secret.CheckIfExists(ctx)
|
k8sSecret.CheckIfExists(ctx)
|
||||||
|
|
||||||
// Get secret
|
// Get secret
|
||||||
secretObj := secret.Get(ctx)
|
secretObj := k8sSecret.Get(ctx)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Working with Deployments
|
### Working with Deployments
|
||||||
|
@@ -60,13 +60,17 @@ func (p *Pod) GetPodLogs(ctx context.Context) string {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
|
Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
|
||||||
|
|
||||||
// Use the first pod found
|
// Find a running pod to get logs from
|
||||||
pod := pods.Items[0]
|
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
|
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
|
// Get logs using the Kubernetes clientset
|
||||||
req := p.clientset.CoreV1().Pods(p.config.Namespace).GetLogs(podName, &corev1.PodLogOptions{})
|
req := p.clientset.CoreV1().Pods(p.config.Namespace).GetLogs(podName, &corev1.PodLogOptions{})
|
||||||
stream, err := req.Stream(context.TODO())
|
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(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())
|
g.Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
|
||||||
|
|
||||||
// Use the first pod found
|
// Find a running pod to verify webhook injection
|
||||||
pod := pods.Items[0]
|
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
|
// Check injection status annotation
|
||||||
g.Expect(pod.Annotations).To(HaveKey("operator.1password.io/status"))
|
g.Expect(pod.Annotations).To(HaveKey("operator.1password.io/status"))
|
||||||
|
Reference in New Issue
Block a user