Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add and test examples directory #111

Merged
merged 26 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 25 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
24 changes: 11 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,17 @@ jobs:
fail-fast: false
matrix:
kindest-image-tag:
- 'v1.19.16'
- 'v1.20.15'
- 'v1.21.14'
- 'v1.22.17'
- 'v1.23.17'
- 'v1.24.13'
- 'v1.25.9'
- 'v1.26.4'
- 'v1.27.2'
- 'v1.21.14@sha256:8a4e9bb3f415d2bb81629ce33ef9c76ba514c14d707f9797a01e3216376ba093'
- 'v1.22.17@sha256:f5b2e5698c6c9d6d0adc419c0deae21a425c07d81bbf3b6a6834042f25d4fba2'
- 'v1.23.17@sha256:59c989ff8a517a93127d4a536e7014d28e235fb3529d9fba91b3951d461edfdb'
- 'v1.24.15@sha256:7db4f8bea3e14b82d12e044e25e34bd53754b7f2b0e9d56df21774e6f66a70ab'
- 'v1.25.11@sha256:227fa11ce74ea76a0474eeefb84cb75d8dad1b08638371ecf0e86259b35be0c8'
- 'v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb'
- 'v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72'
helm-version:
- latest
include:
- kindest-image-tag: 'v1.27.2'
- kindest-image-tag: 'v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72'
helm-version: v3.9.0

steps:
Expand Down Expand Up @@ -78,15 +76,15 @@ jobs:

- id: 'create-kind'
name: Create Kubernetes Cluster with KinD
uses: 'helm/kind-action@v1.7.0'
uses: 'helm/kind-action@v1.8.0'
if: steps.list-changed.outputs.changed == 'true'
with:
node_image: 'kindest/node:${{ matrix.kindest-image-tag }}'
version: 'v0.19.0'
version: 'v0.20.0'

- id: 'test'
name: Run Go Tests
run: 'go test -tags integration ./...'
run: 'go test ./...'
if: steps.list-changed.outputs.changed == 'true'

- id: 'zitadel-test-namespaces'
Expand Down
71 changes: 31 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,28 @@ By default, this chart installs a highly available ZITADEL deployment.

## Install the Chart

Follow the [guide for deploying ZITADEL on Kubernetes](https://zitadel.com/docs/self-hosting/deploy/kubernetes).

## Upgrading from v3

:::Note
Apart from breaking changes in this chart, v4 also update the
default ZITADEL version to v2.14.2. For upgrading
ZITADEL, please refer to the
[ZITADEL release notes](https://github.com/zitadel/zitadel/releases/tag/v2.14.0).

This section is only relevant for existing releases with the
values property cockroachdb.enabled not set to false.

In v4, the cockroachdb chart dependency is removed.
We decided to go this way because:
- Maintaining two separate releases is easier, especially in production.
- We can use Helm hooks specific to ZITADEL.
- ZITADEL doesn't only support CockroachDB.

If you have cockroachdb.enabled=true in your values.yaml,
you need to make sure, that the cockroachdb chart is not
managed by the zitadel release anymore. The following
example for doing so uninstalls your entire zitadel
release, reinstalls cockroach using a dedicated release,
and then installs the new zitadel chart version.
The new cockroach release will take over the PersistentVolumeClaims
from the uninstalled chart, so no data migration is needed.
Nevertheless, we highly recommend making and testing a backup before upgrading.
Also note, that you will have downtime when
following the example while zitadel is uninstalled.

```bash
helm repo add cockroachdb https://charts.cockroachdb.com/
helm repo update cockroachdb zitadel
helm uninstall my-zitadel
helm install crdb cockroachdb/cockroachdb --version 11.0.1 --set fullnameOverride=crdb
helm install my-zitadel zitadel/zitadel --values ./my-zitadel-values.yaml
```
Either follow the [guide for deploying ZITADEL on Kubernetes](https://zitadel.com/docs/self-hosting/deploy/kubernetes) or follow one of the example guides:
- [Insecure Postgres Example](examples/1-postgres-insecure/README.md)
- [Secure Postgres Example](examples/2-postgres-secure/README.md)
- [Insecure Cockroach Example](examples/3-cockroach-insecure/README.md)
- [Secure Cockroach Example](examples/4-cockroach-secure/README.md)
- [Referenced Secrets Example](examples/5-referenced-secrets/README.md)
- [Machine User Setup Example](examples/6-machine-user/README.md)

## Upgrade from v5

- CockroachDB is not in the default configuration anymore.
If you use CockroachDB, please check the host and ssl mode in your ZITADEL Database configuration section.

- The properties for database certificates are renamed and the defaults are removed.
If you use one of the following properties, please check the new names and set the values accordingly:

| Old Value | New Value |
|--------------------------------|-------------------------------|
| `zitadel.dbSslRootCrt` | `zitadel.dbSslCaCrt` |
| `zitadel.dbSslRootCrtSecret` | `zitadel.dbSslCaCrtSecret` |
| `zitadel.dbSslClientCrtSecret` | `zitadel.dbSslAdminCrtSecret` |
| `-` | `zitadel.dbSslUserCrtSecret` |

## Uninstalling the Chart

Expand Down Expand Up @@ -83,10 +67,17 @@ Test the chart:
kind create cluster --image kindest/node:v1.27.2

# Test the chart
go test -tags integration ./...
go test ./...
```

Watch the Kubernetes resources if you want to see progress.
Watch the Kubernetes pods if you want to see progress.

```bash
kubectl get pods --all-namespaces --watch

# Or if you have the watch binary installed
watch -n .1 "kubectl get pods --all-namespaces"
```

## Contributors

Expand Down
1 change: 0 additions & 1 deletion charts/zitadel/.gitignore

This file was deleted.

8 changes: 4 additions & 4 deletions charts/zitadel/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
apiVersion: v2
name: zitadel
description: A Helm chart for ZITADEL v2
description: A Helm chart for ZITADEL
type: application
appVersion: "v2.28.1"
version: 5.0.0
kubeVersion: ">= 1.19.0-0"
appVersion: "v2.35.0"
version: 6.0.0
kubeVersion: ">= 1.21.0-0"
icon: https://zitadel.com/zitadel-logo-dark.svg
maintainers:
- name: zitadel
Expand Down
118 changes: 118 additions & 0 deletions charts/zitadel/acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package acceptance_test

import (
"fmt"
"path/filepath"
"testing"
"time"

"github.com/gruntwork-io/terratest/modules/k8s"
"github.com/stretchr/testify/suite"
"github.com/zitadel/zitadel-charts/charts/zitadel/acceptance"
)

func TestPostgresInsecure(t *testing.T) {
t.Parallel()
example := "1-postgres-insecure"
workDir, values := workingDirectory(example)
suite.Run(t, acceptance.Configure(
t,
newNamespaceIdentifier(example),
acceptance.Postgres.WithValues(filepath.Join(workDir, "postgres-values.yaml")),
[]string{values},
"",
nil,
nil,
nil,
))
}

func TestPostgresSecure(t *testing.T) {
t.Parallel()
example := "2-postgres-secure"
workDir, values := workingDirectory(example)
suite.Run(t, acceptance.Configure(
t,
newNamespaceIdentifier(example),
acceptance.Postgres.WithValues(filepath.Join(workDir, "postgres-values.yaml")),
[]string{values},
"",
func(cfg *acceptance.ConfigurationTest) {
k8s.KubectlApply(t, cfg.KubeOptions, filepath.Join(workDir, "certs-job.yaml"))
k8s.WaitUntilJobSucceed(t, cfg.KubeOptions, "create-certs", 120, 3*time.Second)
},
nil,
nil,
))
}

func TestCockroachInsecure(t *testing.T) {
t.Parallel()
example := "3-cockroach-insecure"
workDir, values := workingDirectory(example)
suite.Run(t, acceptance.Configure(
t,
newNamespaceIdentifier(example),
acceptance.Cockroach.WithValues(filepath.Join(workDir, "cockroach-values.yaml")),
[]string{values},
"",
nil,
nil,
nil,
))
}

func TestCockroachSecure(t *testing.T) {
t.Parallel()
example := "4-cockroach-secure"
workDir, values := workingDirectory(example)
suite.Run(t, acceptance.Configure(
t,
newNamespaceIdentifier(example),
acceptance.Cockroach.WithValues(filepath.Join(workDir, "cockroach-values.yaml")),
[]string{values},
"",
nil,
func(cfg *acceptance.ConfigurationTest) {
k8s.KubectlApply(t, cfg.KubeOptions, filepath.Join(workDir, "zitadel-cert-job.yaml"))
k8s.WaitUntilJobSucceed(t, cfg.KubeOptions, "create-zitadel-cert", 120, 3*time.Second)
},
nil,
))
}

func TestReferencedSecrets(t *testing.T) {
t.Parallel()
example := "5-referenced-secrets"
workDir, values := workingDirectory(example)
suite.Run(t, acceptance.Configure(
t,
newNamespaceIdentifier(example),
acceptance.Postgres.WithValues(filepath.Join(workDir, "postgres-values.yaml")),
[]string{values},
"",
nil,
func(cfg *acceptance.ConfigurationTest) {
k8s.KubectlApply(t, cfg.KubeOptions, filepath.Join(workDir, "zitadel-secrets.yaml"))
k8s.KubectlApply(t, cfg.KubeOptions, filepath.Join(workDir, "zitadel-masterkey.yaml"))
},
nil,
))
}

func TestMachineUser(t *testing.T) {
t.Parallel()
example := "6-machine-user"
workDir, values := workingDirectory(example)
saUserame := readValues(t, values).Zitadel.ConfigmapConfig.FirstInstance.Org.Machine.Machine.Username
suite.Run(t, acceptance.Configure(
t,
newNamespaceIdentifier(example),
acceptance.Postgres.WithValues(filepath.Join(workDir, "postgres-values.yaml")),
[]string{values},
"",
nil,
nil,
testAuthenticatedAPI(saUserame, fmt.Sprintf("%s.json", saUserame))),
)
}
126 changes: 126 additions & 0 deletions charts/zitadel/acceptance/accessibility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package acceptance

import (
"context"
"fmt"
mgmt_api "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
"net/http"
"strings"
"sync"
"time"

"github.com/gruntwork-io/terratest/modules/k8s"
corev1 "k8s.io/api/core/v1"
)

type checkOptions interface {
execute(ctx context.Context) error
}

type checkOptionsFunc func(ctx context.Context) error

func (f checkOptionsFunc) execute(ctx context.Context) error {
return f(ctx)
}

type httpCheckOptions struct {
getUrl string
test func(response *http.Response, body []byte) error
}

func (c *httpCheckOptions) execute(ctx context.Context) (err error) {
checkCtx, checkCancel := context.WithTimeout(ctx, 5*time.Second)
defer checkCancel()
//nolint:bodyclose
resp, body, err := HttpGet(checkCtx, c.getUrl, nil)
if err != nil {
return fmt.Errorf("HttpGet failed with response %+v and body %+v: %w", resp, body, err)
}
if err = c.test(resp, body); err != nil {
return fmt.Errorf("checking response %+v with body %+v failed: %w", resp, body, err)
}
return nil
}

func (s *ConfigurationTest) checkAccessibility(pods []corev1.Pod) {
ctx, cancel := context.WithTimeout(s.Ctx, time.Minute)
defer cancel()
apiBaseURL := s.APIBaseURL()
tunnels := []interface{ Close() }{CloseFunc(ServiceTunnel(s))}
defer func() {
for _, t := range tunnels {
t.Close()
}
}()
checks := append(
zitadelStatusChecks(s.Scheme, s.Domain, s.Port),
&httpCheckOptions{
getUrl: apiBaseURL + "/ui/console/assets/environment.json",
test: func(resp *http.Response, body []byte) error {
if err := checkHttpStatus200(resp, body); err != nil {
return err
}
bodyStr := string(body)
for _, expect := range []string{
fmt.Sprintf(`"api":"%s"`, apiBaseURL),
fmt.Sprintf(`"issuer":"%s"`, apiBaseURL),
} {
if !strings.Contains(bodyStr, expect) {
return fmt.Errorf("couldn't find %s in environment.json content %s", expect, bodyStr)
}
}
return nil
},
},
checkOptionsFunc(func(ctx context.Context) error {
randomInvalidKey := `{"type":"serviceaccount","keyId":"229185755715993707","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAm8bpVfzWJuZsEz1VfTrwSAdkbH+i/u2NS4dv60lwIjtXzrU7\n1xZkHw9jxqz+c+APTaTzp1KY49Dc/wcwXv032FuD1GK2ZSRnMaHm8QnNt8Xhi0e8\nBlu3QQmlqxWPCI67wDPUwXoSHM+r9gQXn2pOR0oonoLP+Gzef+RRj1zUFpZmHWPX\nxw4UWWHwl4xChw9iyO4HbZZGe6wBVYVWe2BnvviCVEeKapyjaCqokZES38S4+S2X\nit202xLlRDyXs3XFWmBzHGmEsxx3LZZor85Kbph/bGjDcV8rdQC1YKC++z8OhuLp\n79GltP7YWrfMN3Z8iRUJQY9APrKQYtljVkWrnQIDAQABAoIBAQCIRZrLyRHCF+LF\ndes6UPvv1t+n9oQtRLxNLV7f0m+Q0p7+yhZeE01kyn67R4yU65YXk0w+vIfZC1a4\nlp5fCl73Gx+ZBP2QPyczCPHRPIVE1Yt33zoByevmrjzKDGMC1nIyMmVVF6eOorFI\n1s2ffEycGqir+b1bEkoWUTJ0Gn3Cf1PE4vTgenHhCrYSvMsbmszQ5GDlfxNj27qf\nF2YrnLx11GplMYU0YEzGqSQHxw76rrmF7yiTvbB+olsjXWARAJxBriSlrF2BDYQk\n+HJ8MEwhWhncaZH1i0Xz/jarDBizpo2o1+K1ZqF6RBUknT72EPnMxI9JsvS4FH44\nZfbrujBhAoGBAMQnx6tO79GpnBIAr7iELyUu5F4mCdU6D0rOAiCjXPpCUAdCDuwX\nzROonIGXPPmhzXXtxebeTz4cf+P8p6tUnrqpl/f0Oi1DMOzv0jL/SAUDC9uUrg6k\nurXZT2dgeONwd1pADyNXSpbZfwRE5IoecFg6cgFi4kune0mdG3mr8QjpAoGBAMtN\nerrMc+4bc3GsmWG4FSXn3xlWMeVGIo2/owP2P5MuMu0ibjofZkl28y0xo8dJgWmv\nLiFSEOhUy+TXZK7K1a2+fD+AXHHaHkBjNbTmCaAbf7rZnuUL4iZVpQyIoTCVuAwo\nC6bsE4TcwGddk4yZj/WZ7v1be+uNgeYwQr2UshyVAoGAN8pYsBCzhR6IlVY8pG50\nOk8sBNss0MjCsLQHRuEwAL37pRTUybG7UmwSl4k8foPWvEP0lcWFJFVWyrGBvulC\nfDTgVFXSdi02LS3Iy1hwU3yaUsnm96NCt5YnT2/Q8l96kuDFbXfWbzFNPxmZJu+h\nZHa7FknZs0rfdgCJYAHXfIECgYEAw3kSqSrNyMICJOkkbO2W/+RLAUx8GwttS8dX\nkQaip/wCoTi6rQ3lxnslY23YIFRPpvL1srn6YbiudrCXMOz7uNtvEYt01082SQha\n6j1IQfZOwLRfb7EWV29/i2aPPWynEqEqWuuf9N5f7MLvjH9WCHpibJ4aryhXHqGG\nekvPWWUCgYA5qDsPk5ykRWEALbunzB/RkpxR6LTLSwriU/OzRswOiKo8UPqH4JZI\nOsFAgudG5H+UOEGMuaSvIq0PLbGex16PjKqUsRwgIoPdH8183f9fxZSJDmr7ELIy\nZJEvE3eJnYwMOpSEZS0VR5Sw0CmKV2Hhd+u6rRB8YjXMP0nAVg8eOA==\n-----END RSA PRIVATE KEY-----\n","userId":"229185755715600491"}`
conn, err := OpenGRPCConnection(s, []byte(randomInvalidKey))
if err != nil {
return fmt.Errorf("couldn't create gRPC management client: %w", err)
}
_, err = conn.Healthz(ctx, &mgmt_api.HealthzRequest{})
// TODO: Why is the key checked on the healthz RPC?
eliobischof marked this conversation as resolved.
Show resolved Hide resolved
if strings.Contains(err.Error(), "Errors.AuthNKey.NotFound") {
err = nil
}
return err
}))
for i := range pods {
pod := pods[i]
port := 8081 + i

podTunnel := k8s.NewTunnel(s.KubeOptions, k8s.ResourceTypePod, pod.Name, port, 8080)
podTunnel.ForwardPort(s.T())
tunnels = append(tunnels, podTunnel)
checks = append(checks, zitadelStatusChecks(s.Scheme, s.Domain, uint16(port))...)
}
wg := sync.WaitGroup{}
for _, check := range checks {
wg.Add(1)
go Await(ctx, s.T(), &wg, 60, check.execute)
}
wait(ctx, s.T(), &wg, "accessibility")
}

func zitadelStatusChecks(scheme, domain string, port uint16) []checkOptions {
return []checkOptions{
&httpCheckOptions{
getUrl: fmt.Sprintf("%s://%s:%d/debug/validate", scheme, domain, port),
test: checkHttpStatus200,
},
&httpCheckOptions{
getUrl: fmt.Sprintf("%s://%s:%d/debug/healthz", scheme, domain, port),
test: checkHttpStatus200,
},
&httpCheckOptions{
getUrl: fmt.Sprintf("%s://%s:%d/debug/ready", scheme, domain, port),
test: checkHttpStatus200,
}}
}

func checkHttpStatus200(resp *http.Response, _ []byte) error {
if resp.StatusCode != 200 {
return fmt.Errorf("expected status code 200 but got %d", resp.StatusCode)
}
return nil
}
Loading