Skip to content

Commit b7c244c

Browse files
Document disaster-recovery test suite (#463)
* Issue #1060 - documents disaster-recovery test suite - details: - Make target to run - test cases with links to described scenarios - disaster-recovery environment setup - also: - unifies Markdown bullet-points elsewhere in disaster-recovery document - tidies up leftover mentions of CR cache (not in scope) - tidies up leftover 'set -x' that polluted script output - removes Docker-over-TLS environment variables from ShellRunner integration - mostly unnecessary * nitpick to retrigger CI * nitpick to retrigger CI * nitpick to retrigger CI * Addresses review comments * Addresses review comment
1 parent f131a59 commit b7c244c

6 files changed

Lines changed: 94 additions & 27 deletions

File tree

docs/content/en/docs/9_troubleshooting_and_faq/disaster-recovery/_index.md

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,31 @@ This document describes the impact on Porch when one or more data stores fails.
1111

1212
Porch has a relatively complex data storage model, such that it essentially acts as a mediator between sets of data stored
1313
in several locations:
14-
* the Kubernetes **cluster control plane** on which Porch is installed, including Porch's **custom resources**
15-
* the **storage repositories** in which package resources are stored (backing the Porch Repository objects). For more details
14+
- the Kubernetes **cluster control plane** on which Porch is installed, including Porch's **custom resources**
15+
- the **storage repositories** in which package resources are stored (backing the Porch Repository objects). For more details
1616
on how Porch interacts with repositories, see the documentation on Repositories and Repository Adapters
1717
<!-- TODO: add links to Repositories and Repository Adapters documentation once each is merged -->
1818
<!-- TODO: Repositories will be ../2_concepts/repositories.md -->
1919
<!-- TODO: Repository Adapters will be ../2_concepts/repositories.md -->
20-
* **Git** repositories are Porch's primary and fully-supported storage backend
21-
* and the contents of the **package revision cache** (which, depending on the cache option configured at install-time, may
22-
be either **incorporated in the cluster's control plane** (with the CR cache) or stored in **a separate SQL database**
23-
(with the DB cache)).
24-
A more detailed explanation of the package revision cache and the different cache options can be found in the Architecture
25-
and Components section: [Cache]({{% relref "/docs/5_architecture_and_components/package-cache/_index.md" %}})
20+
- **Git** repositories are Porch's primary and fully-supported storage backend
21+
- the contents of the **package revision cache**. Depending on the cache option configured at install-time, the package
22+
revision cache may be either **incorporated in the cluster's control plane** (with the CR cache) or stored in **a separate**
23+
**SQL database** (with the DB cache). A more detailed explanation of the package revision cache and the different cache
24+
options can be found in the Architecture and Components section: [Cache]({{% relref "/docs/5_architecture_and_components/package-cache/_index.md" %}}).
2625

2726
Porch's data storage operations are covered in significantly greater depth in the [Architecture and Components section]({{% relref "/docs/5_architecture_and_components/_index.md" %}}).
2827

2928
Each data store serves as the source of truth for different elements of Porch's data structure:
30-
* custom resource objects on Kubernetes control plane:
31-
* Porch repositories (Repository objects)
32-
* package variants (and by extension package variant sets)
33-
* **(if the CR cache is configured)** "work-in-progress" package revisions whose lifecycle stage is "Draft", "Proposed",
29+
- custom resource objects on the Kubernetes control plane:
30+
- Porch repositories (Repository objects)
31+
- package variants (and by extension package variant sets)
32+
- **if the CR cache is configured:** "work-in-progress" package revisions whose lifecycle stage is "Draft", "Proposed",
3433
or "DeletionProposed"
35-
* Git repositories:
36-
* package revisions (Kpt package file contents and directory structures)
37-
* Package revision cache:
38-
* Kubernetes-related metadata for package revisions (e.g. labels and annotations)
39-
* **(if the DB cache is configured)** "work-in-progress" package revisions whose lifecycle stage is "Draft", "Proposed",
34+
- Git repositories:
35+
- package revisions (Kpt package file contents and directory structures)
36+
- Package revision cache:
37+
- Kubernetes-related metadata for package revisions (e.g. labels and annotations)
38+
- **if the DB cache is configured:** "work-in-progress" package revisions whose lifecycle stage is "Draft", "Proposed",
4039
or "DeletionProposed"
4140

4241
## Backup strategy
@@ -60,6 +59,7 @@ Their data and metadata are brought together by the Porch API server from separa
6059
cache.
6160

6261
{{% /alert %}}
62+
6363
{{% alert title="N.B." color="primary" %}}
6464

6565
In the absence of an overarching GitOps solution, the exact mechanism for backing up cluster resources may vary between
@@ -102,6 +102,77 @@ for your specific database!
102102

103103
{{% /alert %}}
104104

105+
106+
## Automated disaster recovery testing
107+
108+
Porch includes an automated disaster-recovery test suite to allow for regression-testing its behaviour under a selection
109+
of data-loss conditions. This suite sets up Porch in a specially-configured Kind cluster environment, creates a large
110+
number of Repository objects and package revisions to provide a representative workload, and verifies
111+
[the disaster scenarios detailed below]({{% relref "#disaster-scenarios" %}}) against it.
112+
113+
### Test suite:
114+
115+
{{% alert title="Caution!" color="warning" %}}
116+
117+
The test suite was developed on a system with the following specifications:
118+
- CPU: 11th Gen Intel(R) Core(TM) i5-1145G7 @ 2.60GHz
119+
- Logical processors: 8
120+
- RAM: 24 GB
121+
- Storage: 250 GiB
122+
123+
Based on observation, the test suite took **an average of ~1 hour for a full run**. Your results may vary based on your
124+
specific system resources; a system with too low a specification **may not even be able to run the suite**.
125+
126+
{{% /alert %}}
127+
128+
#### Prerequisites
129+
- All prerequisites for installation - see the [Installation]({{% relref "/docs/3_getting_started#prerequisites" %}}) section
130+
- All prerequisites for local development - see the [Development Environment]({{% relref "/docs/12_contributing/code-contribution/development-environment#prerequisites" %}})
131+
132+
#### Running the suite
133+
134+
From the root directory of the Porch repository, run:
135+
136+
```bash
137+
make test-disaster-recovery
138+
```
139+
140+
#### Environment details
141+
142+
By default, before running the disaster scenarios, the suite creates, installs, and loads the Kind environment as follows:
143+
144+
- a local Kind cluster (the "data cluster") containing:
145+
- a Git server (Gitea)
146+
- containing several copies of public repositories, each containing a large quantity of sample, test, and catalogued
147+
Kpt packages
148+
- to provide a representative workload for Porch when restoring and reconciling package revision data
149+
- a PostgreSQL instance, set up to allow Porch to connect to and use it for the DB cache
150+
- to make it possible to wipe and restore the database independently of Porch
151+
- another local Kind cluster
152+
- with Porch installed
153+
- with DB cache connected to the PostgreSQL instance installed on the data cluster
154+
- with the `porch-server` microservice's memory limits increased to `4GiB`
155+
- ~115 Repository objects created in Porch, each targeted to a different combination of Git repository and directory within
156+
the repository
157+
- this is calculated to maximize number of package revisions and provide representative workload
158+
- a small number of new package revisions in various lifecycle states, set up using Porch's API to allow testing that they
159+
also will be properly backed up and restored
160+
161+
#### Test cases
162+
163+
The Go-based suite runs the following test cases, each replicating one of the disaster scenarios below:
164+
- `TestCompleteDisaster` - [Complete disaster]({{% relref "#1-complete-disaster" %}})
165+
- `TestKubernetesClusterLoss` - [Kubernetes cluster loss]({{% relref "#2-kubernetes-cluster-loss" %}})
166+
- `TestPorchPodsUngracefulRestart` - [Porch microservices restarted]({{% relref "#3-porch-microservices-restarted" %}})
167+
- `TestDBCacheLossWithBackup` - [DB cache loss with backup]({{% relref "#4-db-cache-loss-with-backup" %}})
168+
- `TestDBCacheLossWithoutBackup` - [DB cache loss without backup]({{% relref "#4-db-cache-loss-without-backup" %}})
169+
170+
For further test development, the disaster-recovery suite's source can be found in the Porch repository at `test/disaster/api/disaster_test.go`.
171+
If updating the tests, please follow the [Code Contribution]({{% relref "/docs/12_contributing/code-contribution" %}})
172+
guide, and, if the updates include changes to this document, the [Documentation Contribution]({{% relref "/docs/12_contributing/docs-contribution" %}})
173+
guide.
174+
175+
105176
## Disaster scenarios
106177

107178
This section gives details of tested scenarios with varying combinations of backup, wipe, and restore routines for different
@@ -299,5 +370,7 @@ All "work in progress" on package revisions lost:
299370
- a similar effect to the use of `porchctl rpkg reject`
300371

301372
{{% alert title="Warning" color="warning" %}}
373+
302374
Having a valid backup of the DB cache database avoids this situation. Porch highly recommends making regular backups.
375+
303376
{{% /alert %}}

test/disaster/api/environment/kind/kind.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ func Reinstall(t *suiteutils.MultiClusterTestSuite) {
5252
if err := installPorchDBCache(t); err != nil {
5353
t.Fatalf("error reinstalling Porch cluster %q: error deploying Porch: %w", dbCacheCluster, err.Error())
5454
}
55-
// UseCRCacheCluster(t)
56-
// if err := installPorchCRCache(t); err != nil {
57-
// t.Fatalf("error reinstalling Porch cluster %q: error deploying Porch: %w", crCacheCluster, err.Error())
58-
// }
5955
}
6056

6157
func UseDataCluster(t *suiteutils.MultiClusterTestSuite) {

test/disaster/api/environment/pods/pods.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func RestartAllPorchPods(t *suiteutils.MultiClusterTestSuite) {
8787
for event := range watcher.ResultChan() {
8888
p, ok := event.Object.(*corev1.Pod)
8989
if !ok {
90-
t.Logf("unexpected type")
90+
t.Logf("Got unexpected type watching porch-server pod")
9191
}
9292
if len(p.Status.ContainerStatuses) > 0 && p.Status.ContainerStatuses[0].Ready == true {
9393
watcher.Stop()

test/disaster/api/environment/porchrepositories/porchrepositories.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func Backup(t *suiteutils.MultiClusterTestSuite) *configapi.RepositoryList {
3434
func Restore(t *suiteutils.MultiClusterTestSuite, repos *configapi.RepositoryList, batchSize int) {
3535
t.T().Helper()
3636

37-
t.Logf("Reconciling %d Porch Repository objects in batches of %d", len(repos.Items), batchSize)
37+
t.Logf("Restoring %d Porch Repository objects in batches of %d", len(repos.Items), batchSize)
3838
for batch := range slices.Chunk(repos.Items, batchSize) {
3939
for _, each := range batch {
4040
t.CreateOrUpdateE(&each)

test/disaster/api/environment/shell/shell.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ func (s ShellRunner) RunCommandLine(command string, args ...string) error {
2828
cmd := exec.Command(command, args...)
2929
cmd.Stdout = os.Stdout
3030
cmd.Stderr = os.Stdout
31-
cmd.Env = append(os.Environ(), "DOCKER_TLS_VERIFY=1", "DOCKER_CERT_PATH=/etc/docker/certs")
3231

3332
if err := cmd.Start(); err != nil {
3433
return err
@@ -44,7 +43,6 @@ func (s ShellRunner) RunCommandLineIntoFile(file string, command string, args ..
4443

4544
os.Setenv("PATH", os.Getenv("PATH")+":"+s.PorchRoot+"/.build/")
4645
cmd := exec.Command(command, args...)
47-
cmd.Env = append(os.Environ(), "DOCKER_TLS_VERIFY=1", "DOCKER_CERT_PATH=/etc/docker/certs")
4846

4947
outfile, err := os.Create(file)
5048
if err != nil {
@@ -65,9 +63,9 @@ func (s ShellRunner) RunCommandLineIntoFile(file string, command string, args ..
6563
}
6664

6765
func (s ShellRunner) RunCommandLineFedFromFile(file string, command string, args ...string) error {
66+
6867
os.Setenv("PATH", os.Getenv("PATH")+":"+s.PorchRoot+"/.build/")
6968
cmd := exec.Command(command, args...)
70-
cmd.Env = append(os.Environ(), "DOCKER_TLS_VERIFY=1", "DOCKER_CERT_PATH=/etc/docker/certs")
7169

7270
infile, err := os.Open(file)
7371
if err != nil {

test/disaster/util/functions.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ dbcache_kubeconfig_file="$self_dir/kubeconfigs/porch_dbcache.conf"
1919

2020
export logs_dir='/tmp/disaster-test-logs_'$$
2121
mkdir -p "$logs_dir"
22-
trap 'set -x; cleanUpLogs' EXIT
22+
trap 'cleanUpLogs' EXIT
2323
function cleanUpLogs() {
2424
h1 "Clean up log pipes..."
2525
rm -frv "$logs_dir"

0 commit comments

Comments
 (0)