Compare commits

..

11 Commits

Author SHA1 Message Date
Andrei Kvapil
941fb02cd1 [cozystack-scheduler] Add custom scheduler as an optional system package (#2205)
## What this PR does

Adds the cozystack-scheduler as an optional system package, vendored
from https://github.com/cozystack/cozystack-scheduler. The scheduler
extends the default kube-scheduler with SchedulingClass-aware affinity
plugins, allowing platform operators to define cluster-wide scheduling
constraints via a SchedulingClass CRD. Pods opt in via the
`scheduler.cozystack.io/scheduling-class` annotation.

The package includes:
- Helm chart with RBAC, ConfigMap, Deployment, and CRD
- PackageSource definition for the cozystack package system
- Optional inclusion in the platform system bundle

### Release note

```release-note
[cozystack-scheduler] Add cozystack-scheduler as an optional system
package. The custom scheduler supports SchedulingClass CRDs for
cluster-wide node affinity, pod affinity, and topology spread constraints.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added cozystack-scheduler component as an optional system package.
* Introduced SchedulingClass custom resource for advanced scheduling
configurations.
* Scheduler supports node affinity, pod affinity, pod anti-affinity, and
topology spread constraints.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-12 13:38:25 +01:00
Andrei Kvapil
bf472fb941 [keycloak-operator] Update to v1.32.0 (#2206)
## What this PR does

Updates the Keycloak Operator Helm chart to v1.32.0 and builds a custom
operator image from upstream master
(https://github.com/epam/edp-keycloak-operator/commit/facbc36)
with the group rename detection patch from PR
https://github.com/epam/edp-keycloak-operator/pull/309 applied on top.

Bumps Keycloak server from 26.0.4 to 26.5.2, which is required by the
new operator client (sends `description` field rejected by older
versions).

Adds SSO session settings (idleTimeout: 86400, maxLifespan: 604800) to
the ClusterKeycloakRealm to match the dashboard client's session
attributes,
as Keycloak 26 enforces realm-level session limits strictly.

Removes `authorizationServicesEnabled` from the dashboard
KeycloakClient,
which is incompatible with Keycloak 26's stricter validation.

### Release note

```release-note
[keycloak-operator] Update the operator to v1.32.0 with group rename fix
(https://github.com/epam/edp-keycloak-operator/pull/309). Bump Keycloak to 26.5.2.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Optional webhook support with cert-manager integration and webhook
service; feature toggles for webhooks and owner refs
* New realm login and session settings, organizations resource/CRD, and
expanded client/user schemas (including ClientRolesV2)

* **Documentation**
* Chart bumped to 1.32.0; README documents new values (clusterDomain,
podLabels, image.registry, securityContext, containerSecurityContext)

* **Security / RBAC**
* RBAC updated to cover organization resources and webhook bindings;
consolidated operator permissions

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-11 18:14:38 +01:00
Kirill Ilin
5e773486e5 feat(extra): add external-dns as standalone extra package (#1988)
## Summary

Add external-dns as a standalone self-managed application in
`packages/extra/external-dns/`, allowing tenants to deploy and configure
their own DNS management directly from the dashboard.

## Motivation

Tenants need the ability to manage their own DNS domains with their own
provider. Following the [developers
guide](https://github.com/cozystack/website/pull/413), this is
implemented as an extra package (like `ingress` and `seaweedfs`) using
the HelmRelease-based pattern, rather than embedding it in the tenant
chart.

This enables multi-tenant scenarios where:
- Tenant A uses Cloudflare for `domain-a.com`
- Tenant B uses AWS Route53 for `domain-b.com`
- Each tenant deploys and manages external-dns independently from the
dashboard

## Changes

- **New package**: `packages/extra/external-dns/` — standalone
HelmRelease-based application
- **New PackageSource**:
`packages/core/platform/sources/external-dns-application.yaml` —
references `system/external-dns` and `extra/external-dns` components
- **Cleaned tenant chart**: removed the previously embedded
`externalDns` block from `packages/apps/tenant/`

## Features

- Support for 9 DNS providers: cloudflare, aws, azure, google,
digitalocean, linode, ovh, exoscale, godaddy
- Per-provider credential configuration with full JSON schema validation
- Domain filtering via `domainFilters`
- Configurable sync policy (`sync` or `upsert-only`)
- Namespaced operation (`namespaced: true`) for tenant isolation
- Unique `txtOwnerId` per namespace to prevent DNS record conflicts
- Resource sizing via presets or explicit CPU/memory

## Usage Example

Deploy from the dashboard, or via values:

```yaml
# Cloudflare
provider: cloudflare
domainFilters:
  - example.com
cloudflare:
  apiToken: "your-cloudflare-api-token"
```

```yaml
# AWS Route53
provider: aws
domainFilters:
  - example.org
aws:
  accessKeyId: "AKIAXXXXXXXX"
  secretAccessKey: "your-secret-key"
  region: "us-east-1"
```

## Test plan

- [ ] `helm template external-dns packages/extra/external-dns/ --set
provider=cloudflare --set cloudflare.apiToken=test` renders correctly
- [ ] `helm template external-dns packages/extra/external-dns/` fails
(provider required)
- [ ] `helm template wrong-name packages/extra/external-dns/ --set
provider=cloudflare` fails (release name check)
- [ ] Deploy external-dns from tenant dashboard
- [ ] Verify HelmRelease is created in tenant namespace with namespaced
RBAC
- [ ] Create an Ingress and verify DNS record is created
- [ ] Verify no conflict with global external-dns instance

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added an External DNS package for automatic DNS record management.
* Support for 9 DNS providers: Cloudflare, AWS, Azure, Google,
DigitalOcean, Linode, OVH, Exoscale, GoDaddy.
* Helm-based deployment with namespaced/system variants and release
configuration options.
* Configurable synchronization policies, domain filtering, provider
credentials, extra args, and resource presets.

* **Documentation**
* New README and schema-driven values documentation for installation and
configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-11 21:58:55 +05:00
Timofei Larkin
cfd57f8c1e [keycloak-operator] Update to v1.32.0
## What this PR does

Updates the Keycloak Operator Helm chart to v1.32.0 and builds a custom
operator image from upstream master (epam/edp-keycloak-operator@facbc36)
with the group rename detection patch from PR
epam/edp-keycloak-operator#309 applied on top.

Bumps Keycloak server from 26.0.4 to 26.5.2, which is required by the
new operator client (sends `description` field rejected by older versions).

Adds SSO session settings (idleTimeout: 86400, maxLifespan: 604800) to
the ClusterKeycloakRealm to match the dashboard client's session attributes,
as Keycloak 26 enforces realm-level session limits strictly.

Removes `authorizationServicesEnabled` from the dashboard KeycloakClient,
which is incompatible with Keycloak 26's stricter validation.

### Release note

```release-note
[keycloak-operator] Update the operator to v1.32.0 with group rename fix
(epam/edp-keycloak-operator#309). Bump Keycloak to 26.5.2.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2026-03-11 15:15:09 +03:00
Andrei Kvapil
7e2c035179 feat(monitoring): migrate VictoriaLogs from VLogs to VLCluster (#2153)
## What this PR does

Migrates VictoriaLogs from the deprecated single-node `VLogs` CR to
`VLCluster` (cluster mode) with vlinsert/vlselect/vlstorage components
for reliability and horizontal scalability.

**Operator upgrade:**
- Upgrades victoria-metrics-operator from v0.55.0 to v0.68.1 to add
VLCluster CRD support

**VLCluster deployment:**
- Replaces `VLogs` (v1beta1) with `VLCluster` (v1) — 2 replicas per
component, consistent with VMCluster
- Adds VPA for all VLCluster components (vlinsert, vlselect, vlstorage)
- Updates WorkloadMonitors for the three-component architecture

**Endpoint updates:**
- Fluent-bit outputs: `vlogs-generic:9428` → `vlinsert-generic:9481`
- Grafana datasource: `vlogs-{name}:9428` → `vlselect-{name}:9471`
- ExternalName service: `vlogs-generic` → `vlinsert-generic`

**Migration (35 → 36):**
- Adds `helm.sh/resource-policy: keep` annotation to existing VLogs
resources so they are preserved during upgrade
- Users need to verify the new VLCluster is working, then optionally
migrate historical data and manually delete old VLogs resources

### Release note

```release-note
[monitoring] Migrate VictoriaLogs from single-node VLogs to VLCluster (cluster mode). Old VLogs resources are preserved with `helm.sh/resource-policy: keep` annotation. After upgrade, verify the new cluster is working, then optionally migrate historical data and delete old VLogs resources manually.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* CRD upgrade workflow with configurable hooks, Job, and ServiceAccount
support
* Monitoring storage split into vlinsert / vlselect / vlstorage with
corresponding VPAs
* Service traffic distribution option and optional shareProcessNamespace
toggle

* **Updates**
  * VictoriaMetrics Operator bumped to v0.68.1
* Fluent Bit and Grafana endpoints/ports updated to new monitoring
targets
  * Global extra-labels support for resources
  * Migration target advanced to version 36
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-11 08:58:19 +01:00
Kirill Ilin
9e166300a7 fix(e2e): update VMCluster status field after operator upgrade
The victoria-metrics-operator v0.68.1 renamed VMCluster status field
from .status.clusterStatus to .status.updateStatus.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-11 09:30:41 +05:00
Timofei Larkin
1dd27f6b23 [cozystack-scheduler] Add custom scheduler as an optional system package
## What this PR does

Adds the cozystack-scheduler as an optional system package, vendored from
https://github.com/cozystack/cozystack-scheduler. The scheduler extends
the default kube-scheduler with SchedulingClass-aware affinity plugins,
allowing platform operators to define cluster-wide scheduling constraints
via a SchedulingClass CRD. Pods opt in via the
`scheduler.cozystack.io/scheduling-class` annotation.

The package includes:
- Helm chart with RBAC, ConfigMap, Deployment, and CRD
- PackageSource definition for the cozystack package system
- Optional inclusion in the platform system bundle

### Release note

```release-note
[cozystack-scheduler] Add cozystack-scheduler as an optional system
package. The custom scheduler supports SchedulingClass CRDs for
cluster-wide node affinity, pod affinity, and topology spread constraints.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2026-03-10 22:43:41 +03:00
Kirill Ilin
b772475ad5 fix(e2e): update VLogs to VLCluster resource in tenant monitoring test
After migrating VictoriaLogs from VLogs to VLCluster, the e2e test
still waited for the old vlogs/generic resource which no longer exists.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-10 22:06:50 +05:00
mattia-eleuteri
f201fe4dac feat(extra): add external-dns as standalone extra package
Signed-off-by: mattia-eleuteri <mattia@hidora.io>
2026-03-10 12:48:01 +01:00
Kirill Ilin
62d36ec4ee feat(monitoring): migrate VictoriaLogs from VLogs to VLCluster
Replace deprecated single-node VLogs CR with VLCluster (cluster mode)
for reliability and horizontal scalability.

Changes:
- Replace VLogs (v1beta1) with VLCluster (v1) using vlinsert/vlselect/vlstorage
- Update fluent-bit outputs to vlinsert-generic:9481
- Update Grafana datasource to vlselect:9471
- Update ExternalName service from vlogs-generic to vlinsert-generic
- Add VPA for all VLCluster components
- Update WorkloadMonitors for three-component architecture
- Add migration 35 to preserve old VLogs resources with keep annotation

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-10 12:04:12 +05:00
Kirill Ilin
a3825314e6 feat(monitoring): upgrade victoria-metrics-operator to v0.68.1
Upgrade from v0.55.0 to v0.68.1 to add VLCluster CRD support,
which is required for migrating VictoriaLogs from single-node
to cluster mode.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-10 12:03:51 +05:00
139 changed files with 29165 additions and 46673 deletions

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bats
teardown() {
kubectl -n tenant-test delete externaldns.apps.cozystack.io --all --ignore-not-found 2>/dev/null || true
}
@test "Create and Verify ExternalDNS with inmemory provider" {
name='test'
kubectl apply -f - <<EOF
apiVersion: apps.cozystack.io/v1alpha1
kind: ExternalDNS
metadata:
name: ${name}
namespace: tenant-test
spec:
provider: inmemory
domainFilters:
- example.com
EOF
sleep 5
kubectl -n tenant-test wait hr ${name} --timeout=120s --for=condition=ready
kubectl -n tenant-test wait hr ${name}-system --timeout=120s --for=condition=ready
timeout 60 sh -ec "until kubectl -n tenant-test get deploy -l app.kubernetes.io/instance=${name}-system -o jsonpath='{.items[0].status.readyReplicas}' | grep -q '1'; do sleep 5; done"
kubectl -n tenant-test delete externaldns.apps.cozystack.io ${name}
}
@test "Create and Verify ExternalDNS with custom annotationPrefix" {
name='test-prefix'
kubectl apply -f - <<EOF
apiVersion: apps.cozystack.io/v1alpha1
kind: ExternalDNS
metadata:
name: ${name}
namespace: tenant-test
spec:
provider: inmemory
annotationPrefix: custom-dns/
domainFilters:
- example.org
EOF
sleep 5
kubectl -n tenant-test wait hr ${name} --timeout=120s --for=condition=ready
kubectl -n tenant-test wait hr ${name}-system --timeout=120s --for=condition=ready
timeout 60 sh -ec "until kubectl -n tenant-test get deploy -l app.kubernetes.io/instance=${name}-system -o jsonpath='{.items[0].status.readyReplicas}' | grep -q '1'; do sleep 5; done"
kubectl -n tenant-test delete externaldns.apps.cozystack.io ${name}
}

View File

@@ -46,6 +46,9 @@ spec:
publishing:
host: "example.org"
apiServerEndpoint: "https://192.168.123.10:6443"
bundles:
enabledPackages:
- cozystack.external-dns-application
EOF
# Wait until HelmReleases appear & reconcile them
@@ -175,8 +178,8 @@ EOF
# VictoriaMetrics components
kubectl wait vmalert/vmalert-shortterm vmalertmanager/alertmanager -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=15m
kubectl wait vlogs/generic -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
kubectl wait vmcluster/shortterm vmcluster/longterm -n tenant-root --for=jsonpath='{.status.clusterStatus}'=operational --timeout=5m
kubectl wait vlclusters/generic -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
kubectl wait vmcluster/shortterm vmcluster/longterm -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
# Grafana
kubectl wait clusters.postgresql.cnpg.io/grafana-db -n tenant-root --for=condition=ready --timeout=5m

View File

@@ -1,4 +1,4 @@
KUBERNETES_VERSIONS = $(shell awk -F'"' '{print $$2}' files/versions.yaml)
KUBERNETES_VERSION = v1.35
KUBERNETES_PKG_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml)
include ../../../hack/common-envs.mk
@@ -15,19 +15,17 @@ update:
image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-csi-driver image-cluster-autoscaler
image-ubuntu-container-disk:
$(foreach ver,$(KUBERNETES_VERSIONS), \
docker buildx build images/ubuntu-container-disk \
--build-arg KUBERNETES_VERSION=$(ver) \
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver)) \
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver)-$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver)) \
--cache-to type=inline \
--metadata-file images/ubuntu-container-disk-$(ver).json \
$(BUILDX_ARGS) && \
echo "$(REGISTRY)/ubuntu-container-disk:$(call settag,$(ver))@$$(yq e '."containerimage.digest"' images/ubuntu-container-disk-$(ver).json -o json -r)" \
> images/ubuntu-container-disk-$(ver).tag && \
rm -f images/ubuntu-container-disk-$(ver).json; \
)
docker buildx build images/ubuntu-container-disk \
--build-arg KUBERNETES_VERSION=${KUBERNETES_VERSION} \
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)) \
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)-$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/ubuntu-container-disk:latest \
--cache-to type=inline \
--metadata-file images/ubuntu-container-disk.json \
$(BUILDX_ARGS)
echo "$(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION))@$$(yq e '."containerimage.digest"' images/ubuntu-container-disk.json -o json -r)" \
> images/ubuntu-container-disk.tag
rm -f images/ubuntu-container-disk.json
image-kubevirt-cloud-provider:
docker buildx build images/kubevirt-cloud-provider \

View File

@@ -1 +0,0 @@
ttl.sh/rjfkdsjflsk/ubuntu-container-disk:v1.30@sha256:8c2276f68beb67edf5bf76d6c97b271dd9303b336e1d5850ae2b91a590c9bb57

View File

@@ -1 +0,0 @@
ttl.sh/rjfkdsjflsk/ubuntu-container-disk:v1.31@sha256:2b631cd227bc9b1bae16de033830e756cd6590b512dc0d2b13367ee626f3e4ca

View File

@@ -1 +0,0 @@
ttl.sh/rjfkdsjflsk/ubuntu-container-disk:v1.32@sha256:600d6ce7df4eaa8cc79c7d6d1b01ecac43e7696beb84eafce752d9210a16455f

View File

@@ -1 +0,0 @@
ttl.sh/rjfkdsjflsk/ubuntu-container-disk:v1.33@sha256:243e55d6f2887a4f6ce8526de52fd083b7b88194d5c7f3eaa51b87efb557ac88

View File

@@ -1 +0,0 @@
ttl.sh/rjfkdsjflsk/ubuntu-container-disk:v1.34@sha256:ad8377d5644ba51729dc69dff4c9f6b4a48957075d054a58c61a45d0bb41f6af

View File

@@ -1 +0,0 @@
ttl.sh/rjfkdsjflsk/ubuntu-container-disk:v1.35@sha256:1c2f2430383a9b9882358c60c194465c1b6092b4aa77536a0343cf74155c0067

View File

@@ -0,0 +1 @@
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:39f626c802dd84f95720ffb54fcd80dfb8a58ac280498870d0a1aa30d4252f94

View File

@@ -74,7 +74,7 @@ spec:
volumes:
- name: system
containerDisk:
image: "{{ $.Files.Get (printf "images/ubuntu-container-disk-%s.tag" $.Values.version) | trim }}"
image: "{{ $.Files.Get "images/ubuntu-container-disk.tag" | trim }}"
- name: ephemeral
emptyDisk:
capacity: {{ .group.ephemeralStorage | default "20Gi" }}
@@ -249,9 +249,6 @@ spec:
joinConfiguration:
nodeRegistration:
kubeletExtraArgs: {}
# Ignore this for 1.31
ignorePreflightErrors:
- FileExisting-conntrack
discovery:
bootstrapToken:
apiServerEndpoint: {{ $.Release.Name }}.{{ $.Release.Namespace }}.svc:6443

View File

@@ -73,8 +73,8 @@ spec:
[OUTPUT]
Name http
Match kube.*
Host vlogs-generic.{{ $targetTenant }}.svc.{{ $clusterDomain }}
port 9428
Host vlinsert-generic.{{ $targetTenant }}.svc.{{ $clusterDomain }}
port 9481
compress gzip
uri /insert/jsonline?_stream_fields=stream,kubernetes_pod_name,kubernetes_container_name,kubernetes_namespace_name&_msg_field=log&_time_field=date
format json_lines

View File

@@ -0,0 +1,21 @@
#!/bin/sh
# Migration 35 --> 36
# Add helm.sh/resource-policy=keep annotation to existing VLogs resources
# so they are preserved when the monitoring helm release upgrades to VLCluster.
# Users will need to manually verify the new cluster is working, then optionally
# migrate historical data and delete old VLogs resources.
set -euo pipefail
VLOGS=$(kubectl get vlogs.operator.victoriametrics.com --all-namespaces --output jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
for resource in $VLOGS; do
NS="${resource%%/*}"
NAME="${resource##*/}"
echo "Adding keep annotation to VLogs/$NAME in $NS"
kubectl annotate vlogs.operator.victoriametrics.com --namespace "$NS" "$NAME" \
helm.sh/resource-policy=keep \
--overwrite
done
kubectl create configmap --namespace cozy-system cozystack-version \
--from-literal=version=36 --dry-run=client --output yaml | kubectl apply --filename -

View File

@@ -0,0 +1,19 @@
---
apiVersion: cozystack.io/v1alpha1
kind: PackageSource
metadata:
name: cozystack.cozystack-scheduler
spec:
sourceRef:
kind: OCIRepository
name: cozystack-packages
namespace: cozy-system
path: /
variants:
- name: default
components:
- name: cozystack-scheduler
path: system/cozystack-scheduler
install:
namespace: kube-system
releaseName: cozystack-scheduler

View File

@@ -0,0 +1,29 @@
---
apiVersion: cozystack.io/v1alpha1
kind: PackageSource
metadata:
name: cozystack.external-dns-application
spec:
sourceRef:
kind: OCIRepository
name: cozystack-packages
namespace: cozy-system
path: /
variants:
- name: default
dependsOn:
- cozystack.networking
libraries:
- name: cozy-lib
path: library/cozy-lib
components:
- name: external-dns-system
path: system/external-dns
- name: external-dns
path: extra/external-dns
libraries: ["cozy-lib"]
- name: external-dns-rd
path: system/external-dns-rd
install:
namespace: cozy-system
releaseName: external-dns-rd

View File

@@ -149,11 +149,13 @@
{{include "cozystack.platform.package.optional.default" (list "cozystack.nfs-driver" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.telepresence" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns-application" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.external-secrets-operator" $) }}
{{- if has "cozystack.bootbox" (default (list) .Values.bundles.enabledPackages) }}
{{include "cozystack.platform.package.default" (list "cozystack.bootbox-application" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.bootbox" $) }}
{{- end }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.hetzner-robotlb" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.cozystack-scheduler" $) }}
{{- end }}

View File

@@ -6,7 +6,7 @@ sourceRef:
migrations:
enabled: false
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.1.0@sha256:d7e8955c1ad8c8fbd4ce42b014c0f849d73d0c3faf0cedaac8e15d647fb2f663
targetVersion: 35
targetVersion: 36
# Bundle deployment configuration
bundles:
system:

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: external-dns
description: External DNS for automatic DNS record management
icon: /logos/external-dns.svg
type: application
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

View File

@@ -0,0 +1,7 @@
NAME=external-dns
include ../../../hack/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -0,0 +1,88 @@
# External DNS
## Parameters
### Common parameters
| Name | Description | Type | Value |
| ------------------ | ---------------------------------------------------------------------------------- | ---------- | ------------- |
| `provider` | DNS provider name. | `string` | `{}` |
| `domainFilters` | List of domains this external-dns instance can manage. | `[]string` | `[]` |
| `policy` | How DNS records are synchronized. | `string` | `upsert-only` |
| `extraArgs` | Extra arguments for external-dns. | `[]string` | `[]` |
| `gatewayAPI` | Enable Gateway API HTTPRoute as a source for DNS records. | `bool` | `false` |
| `annotationPrefix` | Custom annotation prefix for external-dns (useful for running multiple instances). | `string` | `""` |
### Cloudflare
| Name | Description | Type | Value |
| ------------ | -------------------------------- | -------- | ----------------------------------------------------------- |
| `cloudflare` | Cloudflare provider credentials. | `object` | `{"apiEmail":"","apiKey":"","apiToken":"","proxied":false}` |
### AWS
| Name | Description | Type | Value |
| ----- | --------------------------------- | -------- | ------------------------------------------------------------------- |
| `aws` | AWS Route53 provider credentials. | `object` | `{"accessKeyId":"","region":"","secretAccessKey":"","zoneType":""}` |
### Azure
| Name | Description | Type | Value |
| ------- | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `azure` | Azure DNS provider credentials. | `object` | `{"aadClientId":"","aadClientSecret":"","resourceGroup":"","subscriptionId":"","tenantId":""}` |
### Google
| Name | Description | Type | Value |
| -------- | -------------------------------------- | -------- | --------------------------------------- |
| `google` | Google Cloud DNS provider credentials. | `object` | `{"project":"","serviceAccountKey":""}` |
### DigitalOcean
| Name | Description | Type | Value |
| -------------- | -------------------------------------- | -------- | -------------- |
| `digitalocean` | DigitalOcean DNS provider credentials. | `object` | `{"token":""}` |
### Linode
| Name | Description | Type | Value |
| -------- | -------------------------------- | -------- | -------------- |
| `linode` | Linode DNS provider credentials. | `object` | `{"token":""}` |
### OVH
| Name | Description | Type | Value |
| ----- | ----------------------------- | -------- | ----------------------------------------------------------------------------- |
| `ovh` | OVH DNS provider credentials. | `object` | `{"applicationKey":"","applicationSecret":"","consumerKey":"","endpoint":""}` |
### Exoscale
| Name | Description | Type | Value |
| ---------- | ---------------------------------- | -------- | ------------------------------ |
| `exoscale` | Exoscale DNS provider credentials. | `object` | `{"apiKey":"","apiSecret":""}` |
### GoDaddy
| Name | Description | Type | Value |
| --------- | --------------------------------- | -------- | ------------------------------ |
| `godaddy` | GoDaddy DNS provider credentials. | `object` | `{"apiKey":"","apiSecret":""}` |
### Resources
| Name | Description | Type | Value |
| ------------------ | -------------------------------------------------------------------------------------------------------- | ---------- | ------ |
| `resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `resources.cpu` | CPU available to each replica. | `quantity` | `""` |
| `resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` |
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `nano` |

View File

@@ -0,0 +1 @@
../../../library/cozy-lib

View File

@@ -0,0 +1,23 @@
{
"comments": {
"format": "##"
},
"tags": {
"param": "@param",
"section": "@section",
"descriptionStart": "@descriptionStart",
"descriptionEnd": "@descriptionEnd",
"skip": "@skip",
"extra": "@extra"
},
"modifiers": {
"array": "array",
"object": "object",
"string": "string",
"nullable": "nullable",
"default": "default"
},
"regexp": {
"paramsSectionTitle": "Parameters"
}
}

View File

@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width="144" height="144">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#3B82F6"/>
<stop offset="100%" style="stop-color:#1D4ED8"/>
</linearGradient>
</defs>
<rect width="144" height="144" rx="28" fill="url(#bg)"/>
<g transform="translate(72,72)" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<!-- Globe -->
<circle cx="0" cy="-4" r="32"/>
<ellipse cx="0" cy="-4" rx="14" ry="32"/>
<line x1="-32" y1="-4" x2="32" y2="-4"/>
<path d="M-28,12 Q0,18 28,12"/>
<path d="M-28,-20 Q0,-14 28,-20"/>
<!-- Arrow pointing out -->
<line x1="18" y1="18" x2="36" y2="36"/>
<polyline points="28,36 36,36 36,28"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 840 B

View File

@@ -0,0 +1,23 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ .Release.Name }}-dashboard-resources
rules:
- apiGroups:
- helm.toolkit.fluxcd.io
resources:
- helmreleases
resourceNames:
- {{ .Release.Name }}
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: {{ .Release.Name }}-dashboard-resources
subjects:
{{ include "cozy-lib.rbac.subjectsForTenantAndAccessLevel" (list "admin" .Release.Namespace) }}
roleRef:
kind: Role
name: {{ .Release.Name }}-dashboard-resources
apiGroup: rbac.authorization.k8s.io

View File

@@ -0,0 +1,169 @@
{{- if not .Values.provider }}
{{- fail "provider is required: set it to one of cloudflare, aws, azure, google, digitalocean, linode, ovh, exoscale, godaddy, inmemory" }}
{{- end }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Release.Name }}-system
labels:
app.kubernetes.io/instance: {{ .Release.Name }}-system
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
chartRef:
kind: ExternalArtifact
name: cozystack-external-dns-application-default-external-dns-system
namespace: cozy-system
interval: 5m
timeout: 10m
install:
remediation:
retries: -1
upgrade:
force: true
remediation:
retries: -1
values:
external-dns:
namespaced: true
txtOwnerId: {{ .Release.Namespace | quote }}
policy: {{ .Values.policy | quote }}
{{- if .Values.annotationPrefix }}
annotationPrefix: {{ .Values.annotationPrefix | quote }}
{{- end }}
provider:
name: {{ .Values.provider | quote }}
sources:
- ingress
- service
{{- if .Values.gatewayAPI }}
- gateway-httproute
{{- end }}
{{- with .Values.domainFilters }}
domainFilters:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- /* Provider-specific credentials and config */}}
{{- if eq .Values.provider "cloudflare" }}
{{- if or .Values.cloudflare.apiToken (and .Values.cloudflare.apiKey .Values.cloudflare.apiEmail) }}
env:
{{- if .Values.cloudflare.apiToken }}
- name: CF_API_TOKEN
value: {{ .Values.cloudflare.apiToken | quote }}
{{- else }}
- name: CF_API_KEY
value: {{ .Values.cloudflare.apiKey | quote }}
- name: CF_API_EMAIL
value: {{ .Values.cloudflare.apiEmail | quote }}
{{- end }}
{{- end }}
{{- if .Values.cloudflare.proxied }}
cloudflare:
proxied: true
{{- end }}
{{- end }}
{{- if eq .Values.provider "aws" }}
{{- if or .Values.aws.accessKeyId .Values.aws.secretAccessKey .Values.aws.region }}
env:
{{- if .Values.aws.accessKeyId }}
- name: AWS_ACCESS_KEY_ID
value: {{ .Values.aws.accessKeyId | quote }}
{{- end }}
{{- if .Values.aws.secretAccessKey }}
- name: AWS_SECRET_ACCESS_KEY
value: {{ .Values.aws.secretAccessKey | quote }}
{{- end }}
{{- if .Values.aws.region }}
- name: AWS_DEFAULT_REGION
value: {{ .Values.aws.region | quote }}
{{- end }}
{{- end }}
{{- if .Values.aws.zoneType }}
aws:
zoneType: {{ .Values.aws.zoneType | quote }}
{{- end }}
{{- end }}
{{- if eq .Values.provider "azure" }}
{{- if or .Values.azure.tenantId .Values.azure.subscriptionId .Values.azure.resourceGroup .Values.azure.aadClientId .Values.azure.aadClientSecret }}
azure:
tenantId: {{ .Values.azure.tenantId | quote }}
subscriptionId: {{ .Values.azure.subscriptionId | quote }}
resourceGroup: {{ .Values.azure.resourceGroup | quote }}
aadClientId: {{ .Values.azure.aadClientId | quote }}
aadClientSecret: {{ .Values.azure.aadClientSecret | quote }}
{{- end }}
{{- end }}
{{- if eq .Values.provider "digitalocean" }}
{{- if .Values.digitalocean.token }}
env:
- name: DO_TOKEN
value: {{ .Values.digitalocean.token | quote }}
{{- end }}
{{- end }}
{{- if eq .Values.provider "google" }}
{{- if or .Values.google.project .Values.google.serviceAccountKey }}
google:
project: {{ .Values.google.project | quote }}
{{- if .Values.google.serviceAccountKey }}
serviceAccountKey: {{ .Values.google.serviceAccountKey | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- if eq .Values.provider "linode" }}
{{- if .Values.linode.token }}
env:
- name: LINODE_TOKEN
value: {{ .Values.linode.token | quote }}
{{- end }}
{{- end }}
{{- if eq .Values.provider "ovh" }}
{{- if or .Values.ovh.endpoint .Values.ovh.applicationKey .Values.ovh.applicationSecret .Values.ovh.consumerKey }}
env:
- name: OVH_ENDPOINT
value: {{ .Values.ovh.endpoint | quote }}
- name: OVH_APPLICATION_KEY
value: {{ .Values.ovh.applicationKey | quote }}
- name: OVH_APPLICATION_SECRET
value: {{ .Values.ovh.applicationSecret | quote }}
- name: OVH_CONSUMER_KEY
value: {{ .Values.ovh.consumerKey | quote }}
{{- end }}
{{- end }}
{{- if eq .Values.provider "exoscale" }}
{{- if or .Values.exoscale.apiKey .Values.exoscale.apiSecret }}
env:
- name: EXOSCALE_API_KEY
value: {{ .Values.exoscale.apiKey | quote }}
- name: EXOSCALE_API_SECRET
value: {{ .Values.exoscale.apiSecret | quote }}
{{- end }}
{{- end }}
{{- /* extraArgs: merge user-supplied args with provider-specific args (godaddy) */}}
{{- $godaddyArgs := list }}
{{- if eq .Values.provider "godaddy" }}
{{- if .Values.godaddy.apiKey }}
{{- $godaddyArgs = append $godaddyArgs (printf "--godaddy-api-key=%s" .Values.godaddy.apiKey) }}
{{- end }}
{{- if .Values.godaddy.apiSecret }}
{{- $godaddyArgs = append $godaddyArgs (printf "--godaddy-api-secret=%s" .Values.godaddy.apiSecret) }}
{{- end }}
{{- end }}
{{- $allArgs := concat (.Values.extraArgs | default list) $godaddyArgs }}
{{- with $allArgs }}
extraArgs:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with (include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $)) }}
resources:
{{- . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,193 @@
{
"title": "Chart Values",
"type": "object",
"properties": {
"annotationPrefix": {
"description": "Custom annotation prefix for external-dns (useful for running multiple instances).",
"type": "string",
"default": ""
},
"aws": {
"description": "AWS Route53 provider credentials.",
"type": "object",
"default": {
"accessKeyId": "",
"region": "",
"secretAccessKey": "",
"zoneType": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"azure": {
"description": "Azure DNS provider credentials.",
"type": "object",
"default": {
"aadClientId": "",
"aadClientSecret": "",
"resourceGroup": "",
"subscriptionId": "",
"tenantId": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"cloudflare": {
"description": "Cloudflare provider credentials.",
"type": "object",
"default": {
"apiEmail": "",
"apiKey": "",
"apiToken": "",
"proxied": false
},
"x-kubernetes-preserve-unknown-fields": true
},
"digitalocean": {
"description": "DigitalOcean DNS provider credentials.",
"type": "object",
"default": {
"token": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"domainFilters": {
"description": "List of domains this external-dns instance can manage.",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"exoscale": {
"description": "Exoscale DNS provider credentials.",
"type": "object",
"default": {
"apiKey": "",
"apiSecret": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"extraArgs": {
"description": "Extra arguments for external-dns.",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"gatewayAPI": {
"description": "Enable Gateway API HTTPRoute as a source for DNS records.",
"type": "boolean",
"default": false
},
"godaddy": {
"description": "GoDaddy DNS provider credentials.",
"type": "object",
"default": {
"apiKey": "",
"apiSecret": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"google": {
"description": "Google Cloud DNS provider credentials.",
"type": "object",
"default": {
"project": "",
"serviceAccountKey": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"linode": {
"description": "Linode DNS provider credentials.",
"type": "object",
"default": {
"token": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"ovh": {
"description": "OVH DNS provider credentials.",
"type": "object",
"default": {
"applicationKey": "",
"applicationSecret": "",
"consumerKey": "",
"endpoint": ""
},
"x-kubernetes-preserve-unknown-fields": true
},
"policy": {
"description": "How DNS records are synchronized.",
"type": "string",
"default": "upsert-only",
"enum": [
"create-only",
"sync",
"upsert-only"
]
},
"provider": {
"description": "DNS provider name.",
"type": "string",
"enum": [
"cloudflare",
"aws",
"azure",
"google",
"digitalocean",
"linode",
"ovh",
"exoscale",
"godaddy",
"inmemory"
]
},
"resources": {
"description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "CPU available to each replica.",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"x-kubernetes-int-or-string": true
},
"memory": {
"description": "Memory (RAM) available to each replica.",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"x-kubernetes-int-or-string": true
}
}
},
"resourcesPreset": {
"description": "Default sizing preset used when `resources` is omitted.",
"type": "string",
"default": "nano",
"enum": [
"nano",
"micro",
"small",
"medium",
"large",
"xlarge",
"2xlarge"
]
}
}
}

View File

@@ -0,0 +1,149 @@
##
## @section Common parameters
##
## @enum {string} Provider - DNS provider.
## @value cloudflare
## @value aws
## @value azure
## @value google
## @value digitalocean
## @value linode
## @value ovh
## @value exoscale
## @value godaddy
## @value inmemory
## @param {Provider} provider - DNS provider name.
# provider:
## @param {[]string} domainFilters - List of domains this external-dns instance can manage.
domainFilters: []
## @enum {string} Policy - How DNS records are synchronized.
## @value create-only
## @value sync
## @value upsert-only
## @param {Policy} policy="upsert-only" - How DNS records are synchronized.
policy: "upsert-only"
## @param {[]string} extraArgs - Extra arguments for external-dns.
extraArgs: []
## @param {bool} gatewayAPI=false - Enable Gateway API HTTPRoute as a source for DNS records.
gatewayAPI: false
## @param {string} [annotationPrefix] - Custom annotation prefix for external-dns (useful for running multiple instances).
annotationPrefix: ""
##
## @section Cloudflare
##
## @param {object} cloudflare - Cloudflare provider credentials.
cloudflare:
apiToken: ""
apiKey: ""
apiEmail: ""
proxied: false
##
## @section AWS
##
## @param {object} aws - AWS Route53 provider credentials.
aws:
accessKeyId: ""
secretAccessKey: ""
region: ""
zoneType: ""
##
## @section Azure
##
## @param {object} azure - Azure DNS provider credentials.
azure:
tenantId: ""
subscriptionId: ""
resourceGroup: ""
aadClientId: ""
aadClientSecret: ""
##
## @section Google
##
## @param {object} google - Google Cloud DNS provider credentials.
google:
project: ""
serviceAccountKey: ""
##
## @section DigitalOcean
##
## @param {object} digitalocean - DigitalOcean DNS provider credentials.
digitalocean:
token: ""
##
## @section Linode
##
## @param {object} linode - Linode DNS provider credentials.
linode:
token: ""
##
## @section OVH
##
## @param {object} ovh - OVH DNS provider credentials.
ovh:
endpoint: ""
applicationKey: ""
applicationSecret: ""
consumerKey: ""
##
## @section Exoscale
##
## @param {object} exoscale - Exoscale DNS provider credentials.
exoscale:
apiKey: ""
apiSecret: ""
##
## @section GoDaddy
##
## @param {object} godaddy - GoDaddy DNS provider credentials.
godaddy:
apiKey: ""
apiSecret: ""
##
## @section Resources
##
## @typedef {struct} Resources - Explicit CPU and memory configuration.
## @field {quantity} [cpu] - CPU available to each replica.
## @field {quantity} [memory] - Memory (RAM) available to each replica.
## @enum {string} ResourcesPreset - Default sizing preset.
## @value nano
## @value micro
## @value small
## @value medium
## @value large
## @value xlarge
## @value 2xlarge
## @param {Resources} [resources] - Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.
resources: {}
## @param {ResourcesPreset} resourcesPreset="nano" - Default sizing preset used when `resources` is omitted.
resourcesPreset: "nano"

View File

@@ -42,7 +42,9 @@ rules:
- {{ .name }}-vminsert
{{- end }}
{{- range .Values.logsStorages }}
- {{ $.Release.Name }}-vlogs-{{ .name }}
- {{ .name }}-vlstorage
- {{ .name }}-vlselect
- {{ .name }}-vlinsert
{{- end }}
{{- range .Values.metricsStorages }}
- vmalert-{{ .name }}

View File

@@ -67,16 +67,46 @@ spec:
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: vlogs-{{ .name }}
name: {{ .name }}-vlstorage
spec:
replicas: 1
replicas: 2
minReplicas: 1
kind: monitoring
type: vlogs
type: vlstorage
selector:
app.kubernetes.io/component: monitoring
app.kubernetes.io/instance: {{ .name }}
app.kubernetes.io/name: vlogs
app.kubernetes.io/name: vlstorage
version: {{ $.Chart.Version }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ .name }}-vlselect
spec:
replicas: 2
minReplicas: 1
kind: monitoring
type: vlselect
selector:
app.kubernetes.io/component: monitoring
app.kubernetes.io/instance: {{ .name }}
app.kubernetes.io/name: vlselect
version: {{ $.Chart.Version }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ .name }}-vlinsert
spec:
replicas: 2
minReplicas: 1
kind: monitoring
type: vlinsert
selector:
app.kubernetes.io/component: monitoring
app.kubernetes.io/instance: {{ .name }}
app.kubernetes.io/name: vlinsert
version: {{ $.Chart.Version }}
{{- end }}
---

View File

@@ -2,11 +2,11 @@
apiVersion: v1
kind: Service
metadata:
name: vlogs-generic
name: vlinsert-generic
namespace: cozy-monitoring
spec:
type: ExternalName
externalName: vlogs-generic.tenant-root.svc.cluster.local
externalName: vlinsert-generic.tenant-root.svc.cluster.local
---
apiVersion: v1
kind: Service

View File

@@ -0,0 +1,3 @@
apiVersion: v2
name: cozy-cozystack-scheduler
version: 0.1.0

View File

@@ -0,0 +1,10 @@
export NAME=cozystack-scheduler
export NAMESPACE=kube-system
include ../../../hack/package.mk
update:
rm -rf crds templates values.yaml Chart.yaml
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/cozystack/cozystack-scheduler | awk -F'[/^]' 'END{print $$3}') && \
curl -sSL https://github.com/cozystack/cozystack-scheduler/archive/refs/tags/$${tag}.tar.gz | \
tar xzvf - --strip 2 cozystack-scheduler-$${tag#*v}/chart

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cozystack-scheduler
rules:
- apiGroups: ["cozystack.io"]
resources:
- schedulingclasses
verbs: ["get", "list", "watch"]

View File

@@ -0,0 +1,38 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack-scheduler:kube-scheduler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-scheduler
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack-scheduler:volume-scheduler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:volume-scheduler
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack-scheduler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cozystack-scheduler
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}

View File

@@ -0,0 +1,54 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cozystack-scheduler-config
namespace: {{ .Release.Namespace }}
data:
scheduler-config.yaml: |
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: true
resourceNamespace: {{ .Release.Namespace }}
resourceName: cozystack-scheduler
profiles:
- schedulerName: cozystack-scheduler
plugins:
preFilter:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread
- name: CozystackSchedulingClass
filter:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread
- name: CozystackSchedulingClass
preScore:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread
score:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread

View File

@@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: cozystack-scheduler
template:
metadata:
labels:
app: cozystack-scheduler
spec:
serviceAccountName: cozystack-scheduler
containers:
- name: cozystack-scheduler
image: {{ .Values.image }}
command:
- /cozystack-scheduler
- --config=/etc/kubernetes/scheduler-config.yaml
livenessProbe:
httpGet:
path: /healthz
port: 10259
scheme: HTTPS
initialDelaySeconds: 15
volumeMounts:
- name: config
mountPath: /etc/kubernetes/scheduler-config.yaml
subPath: scheduler-config.yaml
readOnly: true
volumes:
- name: config
configMap:
name: cozystack-scheduler-config

View File

@@ -0,0 +1,40 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cozystack-scheduler:extension-apiserver-authentication-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cozystack-scheduler:leader-election
namespace: {{ .Release.Namespace }}
rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["create", "get", "list", "update", "watch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leasecandidates"]
verbs: ["create", "get", "list", "update", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cozystack-scheduler:leader-election
namespace: {{ .Release.Namespace }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cozystack-scheduler:leader-election
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}

View File

@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}

View File

@@ -0,0 +1,2 @@
image: ghcr.io/cozystack/cozystack/cozystack-scheduler:v0.1.0@sha256:5f7150c82177478467ff80628acb5a400291aff503364aa9e26fc346d79a73cf
replicas: 1

View File

@@ -57,7 +57,6 @@ spec:
kind: ClusterKeycloakRealm
secret: $dashboard-client:client-secret-key
advancedProtocolMappers: true
authorizationServicesEnabled: true
name: dashboard
clientId: dashboard
directAccess: true

View File

@@ -0,0 +1,3 @@
apiVersion: v2
name: external-dns-rd
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

View File

@@ -0,0 +1,4 @@
export NAME=external-dns-rd
export NAMESPACE=cozy-system
include ../../../hack/package.mk

View File

@@ -0,0 +1,24 @@
apiVersion: cozystack.io/v1alpha1
kind: ApplicationDefinition
metadata:
name: external-dns
spec:
application:
kind: ExternalDNS
plural: externaldns
singular: externaldns
openAPISchema: |-
{"title":"Chart Values","type":"object","properties":{"annotationPrefix":{"description":"Custom annotation prefix for external-dns (useful for running multiple instances).","type":"string","default":""},"aws":{"description":"AWS Route53 provider credentials.","type":"object","default":{"accessKeyId":"","region":"","secretAccessKey":"","zoneType":""},"x-kubernetes-preserve-unknown-fields":true},"azure":{"description":"Azure DNS provider credentials.","type":"object","default":{"aadClientId":"","aadClientSecret":"","resourceGroup":"","subscriptionId":"","tenantId":""},"x-kubernetes-preserve-unknown-fields":true},"cloudflare":{"description":"Cloudflare provider credentials.","type":"object","default":{"apiEmail":"","apiKey":"","apiToken":"","proxied":false},"x-kubernetes-preserve-unknown-fields":true},"digitalocean":{"description":"DigitalOcean DNS provider credentials.","type":"object","default":{"token":""},"x-kubernetes-preserve-unknown-fields":true},"domainFilters":{"description":"List of domains this external-dns instance can manage.","type":"array","default":[],"items":{"type":"string"}},"exoscale":{"description":"Exoscale DNS provider credentials.","type":"object","default":{"apiKey":"","apiSecret":""},"x-kubernetes-preserve-unknown-fields":true},"extraArgs":{"description":"Extra arguments for external-dns.","type":"array","default":[],"items":{"type":"string"}},"gatewayAPI":{"description":"Enable Gateway API HTTPRoute as a source for DNS records.","type":"boolean","default":false},"godaddy":{"description":"GoDaddy DNS provider credentials.","type":"object","default":{"apiKey":"","apiSecret":""},"x-kubernetes-preserve-unknown-fields":true},"google":{"description":"Google Cloud DNS provider credentials.","type":"object","default":{"project":"","serviceAccountKey":""},"x-kubernetes-preserve-unknown-fields":true},"linode":{"description":"Linode DNS provider credentials.","type":"object","default":{"token":""},"x-kubernetes-preserve-unknown-fields":true},"ovh":{"description":"OVH DNS provider credentials.","type":"object","default":{"applicationKey":"","applicationSecret":"","consumerKey":"","endpoint":""},"x-kubernetes-preserve-unknown-fields":true},"policy":{"description":"How DNS records are synchronized.","type":"string","default":"upsert-only","enum":["create-only","sync","upsert-only"]},"provider":{"description":"DNS provider name.","type":"string","enum":["cloudflare","aws","azure","google","digitalocean","linode","ovh","exoscale","godaddy","inmemory"]},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}
release:
prefix: ""
chartRef:
kind: ExternalArtifact
name: cozystack-external-dns-application-default-external-dns
namespace: cozy-system
dashboard:
category: Networking
singular: External DNS
plural: External DNS
description: External DNS for automatic DNS record management
icon: PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQgMTQ0IiB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCI+CiAgPGRlZnM+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImJnIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzNCODJGNiIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMxRDRFRDgiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjgiIGZpbGw9InVybCgjYmcpIi8+CiAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzIsNzIpIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KICAgIDwhLS0gR2xvYmUgLS0+CiAgICA8Y2lyY2xlIGN4PSIwIiBjeT0iLTQiIHI9IjMyIi8+CiAgICA8ZWxsaXBzZSBjeD0iMCIgY3k9Ii00IiByeD0iMTQiIHJ5PSIzMiIvPgogICAgPGxpbmUgeDE9Ii0zMiIgeTE9Ii00IiB4Mj0iMzIiIHkyPSItNCIvPgogICAgPHBhdGggZD0iTS0yOCwxMiBRMCwxOCAyOCwxMiIvPgogICAgPHBhdGggZD0iTS0yOCwtMjAgUTAsLTE0IDI4LC0yMCIvPgogICAgPCEtLSBBcnJvdyBwb2ludGluZyBvdXQgLS0+CiAgICA8bGluZSB4MT0iMTgiIHkxPSIxOCIgeDI9IjM2IiB5Mj0iMzYiLz4KICAgIDxwb2x5bGluZSBwb2ludHM9IjI4LDM2IDM2LDM2IDM2LDI4Ii8+CiAgPC9nPgo8L3N2Zz4K
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "domainFilters"], ["spec", "policy"], ["spec", "extraArgs"], ["spec", "gatewayAPI"], ["spec", "annotationPrefix"], ["spec", "cloudflare"], ["spec", "cloudflare", "apiToken"], ["spec", "cloudflare", "apiKey"], ["spec", "cloudflare", "apiEmail"], ["spec", "cloudflare", "proxied"], ["spec", "aws"], ["spec", "aws", "accessKeyId"], ["spec", "aws", "secretAccessKey"], ["spec", "aws", "region"], ["spec", "aws", "zoneType"], ["spec", "azure"], ["spec", "azure", "tenantId"], ["spec", "azure", "subscriptionId"], ["spec", "azure", "resourceGroup"], ["spec", "azure", "aadClientId"], ["spec", "azure", "aadClientSecret"], ["spec", "google"], ["spec", "google", "project"], ["spec", "google", "serviceAccountKey"], ["spec", "digitalocean"], ["spec", "digitalocean", "token"], ["spec", "linode"], ["spec", "linode", "token"], ["spec", "ovh"], ["spec", "ovh", "endpoint"], ["spec", "ovh", "applicationKey"], ["spec", "ovh", "applicationSecret"], ["spec", "ovh", "consumerKey"], ["spec", "exoscale"], ["spec", "exoscale", "apiKey"], ["spec", "exoscale", "apiSecret"], ["spec", "godaddy"], ["spec", "godaddy", "apiKey"], ["spec", "godaddy", "apiSecret"], ["spec", "resources"], ["spec", "resourcesPreset"]]

View File

@@ -0,0 +1,4 @@
{{- range $path, $_ := .Files.Glob "cozyrds/*" }}
---
{{ $.Files.Get $path }}
{{- end }}

View File

@@ -0,0 +1 @@
{}

View File

@@ -21,3 +21,7 @@
.idea/
*.tmproj
.vscode/
ci/
schema/
.schema.yaml
tests/

View File

@@ -18,11 +18,117 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [UNRELEASED]
## [v1.15.0] - 2023-09-10
## [v1.20.0]
### Added
- Add option to set `annotationPrefix` ([#5889](https://github.com/kubernetes-sigs/external-dns/pull/5889)) _@lexfrei_
### Changed
- Updated _ExternalDNS_ OCI image version to [v0.15.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.15.0). ([#xxxx](https://github.com/kubernetes-sigs/external-dns/pull/xxxx)) _@stevehipwell_
- Grant `networking.k8s.io/ingresses` and `gateway.solo.io/gateways` permissions when using `gloo-proxy` source. ([#5909](https://github.com/kubernetes-sigs/external-dns/pull/5909)) _@cucxabong_
- Update _ExternalDNS_ OCI image version to [v0.20.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.20.0). ([#6005](https://github.com/kubernetes-sigs/external-dns/pull/6005)) _@vflaux_
### Fixed
- Fixed the missing schema for `.provider.webhook.serviceMonitor` configs ([#5932](https://github.com/kubernetes-sigs/external-dns/pull/5932)) _@chrisbsmith_
- Fixed incorrect indentation of selector labels under `spec.template.spec.topologySpreadConstraints` when `topologySpreadConstraints` is set. ([#6054](https://github.com/kubernetes-sigs/external-dns/pull/6054)) _@andylim0221_
## [v1.19.0] - 2025-09-08
### Added
- Add option to configure `annotationFilter` via dedicated chart value. ([#5737](https://github.com/kubernetes-sigs/external-dns/pull/5737)) _@dshatokhin_
### Changed
- Grant `discovery.k8s.io/endpointslices` permission only when using `service` source. ([#5746](https://github.com/kubernetes-sigs/external-dns/pull/5746)) _@vflaux_
- Update _ExternalDNS_ OCI image version to [v0.19.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.19.0). ([#5819](https://github.com/kubernetes-sigs/external-dns/pull/5819)) _@stevehipwell_
## [v1.18.0] - 2025-07-14
### Changed
- Update RBAC for `Service` source to support `EndpointSlices`. ([#5493](https://github.com/kubernetes-sigs/external-dns/pull/5493)) _@vflaux_
- Update _ExternalDNS_ OCI image version to [v0.18.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.18.0). ([#5633](https://github.com/kubernetes-sigs/external-dns/pull/5633)) _@elafarge_
### Fixed
- Fixed the lack of schema support for `create-only` dns policy in helm values ([#5627](https://github.com/kubernetes-sigs/external-dns/pull/5627)) _@coltonhughes_
- Fixed the type of `.extraContainers` from `object` to `list` (array). ([#5564](https://github.com/kubernetes-sigs/external-dns/pull/5564)) _@svengreb_
## [v1.17.0] - 2025-06-04
### Changed
- Allow extraArgs to also be a map enabling overrides of individual values. ([#5293](https://github.com/kubernetes-sigs/external-dns/pull/5293)) _@frittentheke_
- Update CRD. ([#5287](https://github.com/kubernetes-sigs/external-dns/pull/5287)) _@mloiseleur_
- Update CRD. ([#5446](https://github.com/kubernetes-sigs/external-dns/pull/5446)) _@mloiseleur_
- Update _ExternalDNS_ OCI image version to [v0.17.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.17.0). ([#5479](https://github.com/kubernetes-sigs/external-dns/pull/5479)) _@stevehipwell_
### Fixed
- Fix wrong type definitions for webhook probes. ([#5297](https://github.com/kubernetes-sigs/external-dns/pull/5297)) _@semnell_
- Update schema with latest plugin release. ([#5510](https://github.com/kubernetes-sigs/external-dns/pull/5510)) _@mloiseleur
## [v1.16.1] - 2025-04-10
### Changed
- Set defaults for `automountServiceAccountToken` and `serviceAccount.automountServiceAccountToken` to `true` in Helm chart values. ([#5207](https://github.com/kubernetes-sigs/external-dns/pull/5207)) _@t3mi_
### Fixed
- Correctly handle `txtPrefix` and `txtSuffix` arguments when both are provided. ([#5250](https://github.com/kubernetes-sigs/external-dns/pull/5250)) _@ivankatliarchuk_
- Add missing types in the schema for empty values. ([#5228](https://github.com/kubernetes-sigs/external-dns/pull/5228)) _@ivankatliarchuk_
- Add missing types in the schema for empty values. ([#5207](https://github.com/kubernetes-sigs/external-dns/pull/5207)) _@t3mi_
## [v1.16.0] - 2025-03-20
### Added
- Add helm testing framework `helm plugin unittest`. ([#5137](https://github.com/kubernetes-sigs/external-dns/pull/5137)) _@ivankatliarchuk_
- Add ability to generate schema with `helm plugin schema`. ([#5075](https://github.com/kubernetes-sigs/external-dns/pull/5075)) _@ivankatliarchuk_
- Add `docs/contributing/dev-guide.md#helm-values` guide. ([#5075](https://github.com/kubernetes-sigs/external-dns/pull/5075)) _@ivankatliarchuk_
### Changed
- Regenerate JSON schema with `helm-values-schema-json' plugin. ([#5075](https://github.com/kubernetes-sigs/external-dns/pull/5075)) _@ivankatliarchuk_
- Update _ExternalDNS_ OCI image version to [v0.16.1](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.16.1). ([#5201](https://github.com/kubernetes-sigs/external-dns/pull/5201)) _@stevehipwell_
## [v1.15.2] - 2025-02-14
### Changed
- Added `transportservers` resource to ClusterRole when specifying `f5-transportserver` or `f5-virtualserver` as a source. ([#5066](https://github.com/kubernetes-sigs/external-dns/pull/5066)) _@visokoo_
### Fixed
- Fixed handling of non-string types in `serviceAccount.metadata.annotations` field. ([#5067](https://github.com/kubernetes-sigs/external-dns/pull/5067)) _@hjoshi123_
- Fixed regression where `affinity.nodeAffinity` was being ignored. ([#5046](https://github.com/kubernetes-sigs/external-dns/pull/5046)) _@mkhpalm_
## [v1.15.1] - 2025-01-27
### Added
- Added ability to configure `imagePullSecrets` via helm `global` value. ([#4667](https://github.com/kubernetes-sigs/external-dns/pull/4667)) _@jkroepke_
- Added options to configure `labelFilter` and `managedRecordTypes` via dedicated helm values. ([#4849](https://github.com/kubernetes-sigs/external-dns/pull/4849)) _@abaguas_
### Changed
- Allow templating `serviceaccount.annotations` keys and values, by rendering them using the `tpl` built-in function. ([#4958](https://github.com/kubernetes-sigs/external-dns/pull/4958)) _@fcrespofastly_
- Updated _ExternalDNS_ OCI image version to [v0.15.1](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.15.1). ([#5028](https://github.com/kubernetes-sigs/external-dns/pull/5028)) _@stevehipwell_
### Fixed
- Fixed automatic addition of pod selector labels to `affinity` and `topologySpreadConstraints` if not defined. ([#4666](https://github.com/kubernetes-sigs/external-dns/pull/4666)) _@pvickery-ParamountCommerce_
- Fixed missing Ingress permissions when using Istio sources. ([#4845](https://github.com/kubernetes-sigs/external-dns/pull/4845)) _@joekhoobyar_
## [v1.15.0] - 2024-09-11
### Changed
- Updated _ExternalDNS_ OCI image version to [v0.15.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.15.0). ([#4735](https://github.com/kubernetes-sigs/external-dns/pull/4735)) _@stevehipwell_
### Fixed
@@ -31,7 +137,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed to add correct webhook metric port to `Service` and `ServiceMonitor`. ([#4643](https://github.com/kubernetes-sigs/external-dns/pull/4643)) _@kimsondrup_
- Fixed to no longer require the unauthenticated webhook provider port to be exposed for health probes. ([#4691](https://github.com/kubernetes-sigs/external-dns/pull/4691)) _@kimsondrup_ & _@hatrx_
## [v1.14.5] - 2023-06-10
## [v1.14.5] - 2024-06-10
### Added
@@ -48,7 +154,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed the `ServiceMonitor` job name to correctly use the instance label. ([#4541](https://github.com/kubernetes-sigs/external-dns/pull/4541)) _@stevehipwell_
## [v1.14.4] - 2023-04-03
## [v1.14.4] - 2024-04-05
### Added
@@ -59,7 +165,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated _ExternalDNS_ OCI image version to [v0.14.1](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.14.1). ([#4357](https://github.com/kubernetes-sigs/external-dns/pull/4357)) _@stevehipwell_
## [v1.14.3] - 2023-01-26
## [v1.14.3] - 2024-01-26
### Fixed
@@ -73,7 +179,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Restore template support in `.Values.provider` and `.Values.provider.name`
## [v1.14.1] - 2024-01-11
## [v1.14.1] - 2024-01-12
### Fixed
@@ -97,7 +203,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `secretConfiguration` value has been deprecated in favour of creating secrets external to the Helm chart and configuring their use via the `extraVolumes` & `extraVolumeMounts` values. ([#4161](https://github.com/kubernetes-sigs/external-dns/pull/4161)) [@stevehipwell](https://github.com/stevehipwell)
## [v1.13.1] - 2023-09-07
## [v1.13.1] - 2023-09-08
### Added
@@ -200,6 +306,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
RELEASE LINKS
-->
[UNRELEASED]: https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns
[v1.20.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.20.0
[v1.19.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.19.0
[v1.18.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.18.0
[v1.17.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.17.0
[v1.16.1]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.16.1
[v1.16.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.16.0
[v1.15.2]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.15.2
[v1.15.1]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.15.1
[v1.15.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.15.0
[v1.14.5]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.14.5
[v1.14.4]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.14.4

View File

@@ -1,28 +1,30 @@
annotations:
artifacthub.io/changes: |
artifacthub.io/changes: |-
- kind: added
description: "Add option to set annotationPrefix (#5889) @lexfrei."
- kind: changed
description: "Updated _ExternalDNS_ OCI image version to [v0.15.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.15.0)."
description: "Grant networking.k8s.io/ingresses and gateway.solo.io/gateways permissions when using gloo-proxy source."
- kind: changed
description: "Update ExternalDNS OCI image version to v0.20.0."
- kind: fixed
description: "Fixed `provider.webhook.resources` behavior to correctly leverage resource limits."
description: "Fixed the missing schema for .provider.webhook."
- kind: fixed
description: "Fixed `provider.webhook.imagePullPolicy` behavior to correctly leverage pull policy."
- kind: fixed
description: "Fixed to add correct webhook metric port to `Service` and `ServiceMonitor`."
- kind: fixed
description: "Fixed to no longer require the unauthenticated webhook provider port to be exposed for health probes."
description: "Fixed incorrect indentation of selector labels under spec.template.spec.topologySpreadConstraints when topologySpreadConstraints is set."
apiVersion: v2
appVersion: 0.15.0
appVersion: 0.20.0
description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with
DNS providers.
home: https://github.com/kubernetes-sigs/external-dns/
icon: https://github.com/kubernetes-sigs/external-dns/raw/master/docs/img/external-dns.png
keywords:
- kubernetes
- k8s
- externaldns
- external-dns
- dns
- service
- ingress
- gateway
maintainers:
- email: steve.hipwell@gmail.com
name: stevehipwell
@@ -30,4 +32,4 @@ name: external-dns
sources:
- https://github.com/kubernetes-sigs/external-dns/
type: application
version: 1.15.0
version: 1.20.0

View File

@@ -1,6 +1,6 @@
# external-dns
![Version: 1.15.0](https://img.shields.io/badge/Version-1.15.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.15.0](https://img.shields.io/badge/AppVersion-0.15.0-informational?style=flat-square)
![Version: 1.20.0](https://img.shields.io/badge/Version-1.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.20.0](https://img.shields.io/badge/AppVersion-0.20.0-informational?style=flat-square)
ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
@@ -27,12 +27,15 @@ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
After you've installed the repo you can install the chart.
```shell
helm upgrade --install external-dns external-dns/external-dns --version 1.15.0
helm upgrade --install external-dns external-dns/external-dns --version 1.20.0
```
## Providers
Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the `provider.<name>.<key>` values, where supported, and the `extraArgs` value. For legacy support `provider` can be set to the name of the provider with all additional configuration being set via the `extraArgs` value.
> Legacy support of setting `provider: <name>` is deprecated.
Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the `provider.<name>.<key>` values, where supported, and the `extraArgs` value.
See [documentation](https://kubernetes-sigs.github.io/external-dns/#new-providers) for more info on available providers and tutorials.
### Providers with Specific Configuration Support
@@ -45,13 +48,13 @@ See [documentation](https://kubernetes-sigs.github.io/external-dns/#new-provider
For set up for a specific provider using the Helm chart, see the following links:
- [AWS](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md#using-helm-with-oidc)
- [akamai-edgedns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/akamai-edgedns.md#using-helm)
- [cloudflare](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/cloudflare.md#using-helm)
- [digitalocean](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/digitalocean.md#using-helm)
- [godaddy](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/godaddy.md#using-helm)
- [ns1](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/ns1.md#using-helm)
- [plural](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/plural.md#using-helm)
* [AWS](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md#using-helm-with-oidc)
* [akamai-edgedns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/akamai-edgedns.md#using-helm)
* [cloudflare](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/cloudflare.md#using-helm)
* [digitalocean](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/digitalocean.md#using-helm)
* [godaddy](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/godaddy.md#using-helm)
* [ns1](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/ns1.md#using-helm)
* [plural](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/plural.md#using-helm)
## Namespaced Scoped Installation
@@ -91,36 +94,43 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| affinity | object | `{}` | Affinity settings for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). If an explicit label selector is not provided for pod affinity or pod anti-affinity one will be created from the pod selector labels. |
| automountServiceAccountToken | bool | `nil` | Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `Pod`. |
| annotationFilter | string | `nil` | Filter resources queried for endpoints by annotation selector. |
| annotationPrefix | string | `nil` | Annotation prefix for external-dns annotations (useful for split horizon DNS with multiple instances). |
| automountServiceAccountToken | bool | `true` | Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `Pod`. |
| commonLabels | object | `{}` | Labels to add to all chart resources. |
| deploymentAnnotations | object | `{}` | Annotations to add to the `Deployment`. |
| deploymentStrategy | object | `{"type":"Recreate"}` | [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). |
| dnsConfig | object | `nil` | [DNS config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config) for the pod, if not set the default will be used. |
| dnsPolicy | string | `nil` | [DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod, if not set the default will be used. |
| domainFilters | list | `[]` | |
| domainFilters | list | `[]` | Limit possible target zones by domain suffixes. |
| enabled | bool | `nil` | No effect - reserved for use in sub-charting. |
| env | list | `[]` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `external-dns` container. |
| excludeDomains | list | `[]` | |
| extraArgs | list | `[]` | Extra arguments to provide to _ExternalDNS_. |
| extraContainers | object | `{}` | Extra containers to add to the `Deployment`. |
| excludeDomains | list | `[]` | Intentionally exclude domains from being managed. |
| extraArgs | object | `{}` | Extra arguments to provide to _ExternalDNS_. An array or map can be used, with maps allowing for value overrides; maps also support slice values to use the same arg multiple times. |
| extraContainers | list | `[]` | Extra containers to add to the `Deployment`. |
| extraVolumeMounts | list | `[]` | Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `external-dns` container. |
| extraVolumes | list | `[]` | Extra [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) for the `Pod`. |
| fullnameOverride | string | `nil` | Override the full name of the chart. |
| gatewayNamespace | string | `nil` | _Gateway API_ gateway namespace to watch. |
| global.imagePullSecrets | list | `[]` | Global image pull secrets. |
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy for the `external-dns` container. |
| image.repository | string | `"registry.k8s.io/external-dns/external-dns"` | Image repository for the `external-dns` container. |
| image.tag | string | `nil` | Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set. |
| imagePullSecrets | list | `[]` | Image pull secrets. |
| initContainers | list | `[]` | [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) to add to the `Pod` definition. |
| interval | string | `"1m"` | Interval for DNS updates. |
| labelFilter | string | `nil` | Filter resources queried for endpoints by label selector. |
| livenessProbe | object | See _values.yaml_ | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container. |
| logFormat | string | `"text"` | Log format. |
| logLevel | string | `"info"` | Log level. |
| managedRecordTypes | list | `[]` | Record types to manage (default: A, AAAA, CNAME) |
| nameOverride | string | `nil` | Override the name of the chart. |
| namespaced | bool | `false` | if `true`, _ExternalDNS_ will run in a namespaced scope (`Role`` and `Rolebinding`` will be namespaced too). |
| nodeSelector | object | `{}` | Node labels to match for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). |
| podAnnotations | object | `{}` | Annotations to add to the `Pod`. |
| podLabels | object | `{}` | Labels to add to the `Pod`. |
| podSecurityContext | object | See _values.yaml_ | [Pod security context](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core), this supports full customisation. |
| policy | string | `"upsert-only"` | How DNS records are synchronized between sources and providers; available values are `sync` & `upsert-only`. |
| policy | string | `"upsert-only"` | How DNS records are synchronized between sources and providers; available values are `create-only`, `sync`, & `upsert-only`. |
| priorityClassName | string | `nil` | Priority class name for the `Pod`. |
| provider.name | string | `"aws"` | _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers). |
| provider.webhook.args | list | `[]` | Extra arguments to provide for the `webhook` container. |
@@ -147,11 +157,11 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| secretConfiguration.subPath | string | `nil` | Sub-path for mounting the `Secret`, this can be templated. |
| securityContext | object | See _values.yaml_ | [Security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `external-dns` container. |
| service.annotations | object | `{}` | Service annotations. |
| service.ipFamilies | list | `[]` | Service IP families. |
| service.ipFamilies | list | `[]` | Service IP families (e.g. IPv4 and/or IPv6). |
| service.ipFamilyPolicy | string | `nil` | Service IP family policy. |
| service.port | int | `7979` | Service HTTP port. |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. |
| serviceAccount.automountServiceAccountToken | string | `nil` | Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `ServiceAccount`. |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. Templates are allowed in both the key and the value. Example: `example.com/annotation/{{ .Values.nameOverride }}: {{ .Values.nameOverride }}` |
| serviceAccount.automountServiceAccountToken | bool | `true` | Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `ServiceAccount`. |
| serviceAccount.create | bool | `true` | If `true`, create a new `ServiceAccount`. |
| serviceAccount.labels | object | `{}` | Labels to add to the service account. |
| serviceAccount.name | string | `nil` | If this is set and `serviceAccount.create` is `true` this will be used for the created `ServiceAccount` name, if set and `serviceAccount.create` is `false` then this will define an existing `ServiceAccount` to use. |
@@ -173,7 +183,7 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| tolerations | list | `[]` | Node taints which will be tolerated for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). |
| topologySpreadConstraints | list | `[]` | Topology spread constraints for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). If an explicit label selector is not provided one will be created from the pod selector labels. |
| triggerLoopOnEvent | bool | `false` | If `true`, triggers run loop on create/update/delete events in addition of regular interval. |
| txtOwnerId | string | `nil` | Specify an identifier for this instance of _ExternalDNS_ wWhen using a registry other than `noop`. |
| txtOwnerId | string | `nil` | Specify an identifier for this instance of _ExternalDNS_ when using a registry other than `noop`. |
| txtPrefix | string | `nil` | Specify a prefix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtSuffix`. |
| txtSuffix | string | `nil` | Specify a suffix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtPrefix`. |

View File

@@ -27,7 +27,10 @@ helm upgrade --install {{ template "chart.name" . }} external-dns/{{ template "c
## Providers
Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the `provider.<name>.<key>` values, where supported, and the `extraArgs` value. For legacy support `provider` can be set to the name of the provider with all additional configuration being set via the `extraArgs` value.
> Legacy support of setting `provider: <name>` is deprecated.
Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the `provider.<name>.<key>` values, where supported, and the `extraArgs` value.
See [documentation](https://kubernetes-sigs.github.io/external-dns/#new-providers) for more info on available providers and tutorials.
### Providers with Specific Configuration Support
@@ -40,13 +43,13 @@ See [documentation](https://kubernetes-sigs.github.io/external-dns/#new-provider
For set up for a specific provider using the Helm chart, see the following links:
- [AWS](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md#using-helm-with-oidc)
- [akamai-edgedns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/akamai-edgedns.md#using-helm)
- [cloudflare](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/cloudflare.md#using-helm)
- [digitalocean](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/digitalocean.md#using-helm)
- [godaddy](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/godaddy.md#using-helm)
- [ns1](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/ns1.md#using-helm)
- [plural](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/plural.md#using-helm)
* [AWS](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md#using-helm-with-oidc)
* [akamai-edgedns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/akamai-edgedns.md#using-helm)
* [cloudflare](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/cloudflare.md#using-helm)
* [digitalocean](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/digitalocean.md#using-helm)
* [godaddy](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/godaddy.md#using-helm)
* [ns1](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/ns1.md#using-helm)
* [plural](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/plural.md#using-helm)
## Namespaced Scoped Installation

View File

@@ -1,10 +1,13 @@
### Added
- Add option to set `annotationPrefix` ([#5889](https://github.com/kubernetes-sigs/external-dns/pull/5889)) _@lexfrei_
### Changed
- Updated _ExternalDNS_ OCI image version to [v0.15.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.15.0). ([#xxxx](https://github.com/kubernetes-sigs/external-dns/pull/xxxx)) _@stevehipwell_
- Grant `networking.k8s.io/ingresses` and `gateway.solo.io/gateways` permissions when using `gloo-proxy` source. ([#5909](https://github.com/kubernetes-sigs/external-dns/pull/5909)) _@cucxabong_
- Update _ExternalDNS_ OCI image version to [v0.20.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.20.0). ([#6005](https://github.com/kubernetes-sigs/external-dns/pull/6005)) _@vflaux_
### Fixed
- Fixed `provider.webhook.resources` behavior to correctly leverage resource limits. ([#4560](https://github.com/kubernetes-sigs/external-dns/pull/4560)) _@crutonjohn_
- Fixed `provider.webhook.imagePullPolicy` behavior to correctly leverage pull policy. ([#4643](https://github.com/kubernetes-sigs/external-dns/pull/4643)) _@kimsondrup_
- Fixed to add correct webhook metric port to `Service` and `ServiceMonitor`. ([#4643](https://github.com/kubernetes-sigs/external-dns/pull/4643)) _@kimsondrup_
- Fixed to no longer require the unauthenticated webhook provider port to be exposed for health probes. ([#4691](https://github.com/kubernetes-sigs/external-dns/pull/4691)) _@kimsondrup_ & _@hatrx_
- Fixed the missing schema for `.provider.webhook.serviceMonitor` configs ([#5932](https://github.com/kubernetes-sigs/external-dns/pull/5932)) _@chrisbsmith_
- Fixed incorrect indentation of selector labels under `spec.template.spec.topologySpreadConstraints` when `topologySpreadConstraints` is set. ([#6054](https://github.com/kubernetes-sigs/external-dns/pull/6054)) _@andylim0221_

View File

@@ -1,2 +0,0 @@
provider:
name: inmemory

View File

@@ -1,9 +1,9 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: dnsendpoints.externaldns.k8s.io
annotations:
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/external-dns/pull/2007
name: dnsendpoints.externaldns.k8s.io
spec:
group: externaldns.k8s.io
names:
@@ -16,6 +16,9 @@ spec:
- name: v1alpha1
schema:
openAPIV3Schema:
description: |-
DNSEndpoint is a contract that a user-specified CRD must implement to be used as a source for external-dns.
The user-specified CRD should also have the status sub-resource.
properties:
apiVersion:
description: |-
@@ -39,9 +42,7 @@ spec:
properties:
endpoints:
items:
description:
Endpoint is a high-level way of a connection between
a service and an IP
description: Endpoint is a high-level way of a connection between a service and an IP
properties:
dnsName:
description: The hostname of the DNS record
@@ -54,9 +55,7 @@ spec:
providerSpecific:
description: ProviderSpecific stores provider specific config
items:
description:
ProviderSpecificProperty holds the name and value
of a configuration which is specific to individual DNS providers
description: ProviderSpecificProperty holds the name and value of a configuration which is specific to individual DNS providers
properties:
name:
type: string
@@ -69,15 +68,10 @@ spec:
format: int64
type: integer
recordType:
description:
RecordType type of record, e.g. CNAME, A, AAAA,
SRV, TXT etc
description: RecordType type of record, e.g. CNAME, A, AAAA, SRV, TXT etc
type: string
setIdentifier:
description:
Identifier to distinguish multiple records with
the same name and type (e.g. Route53 records with routing
policies other than 'simple')
description: Identifier to distinguish multiple records with the same name and type (e.g. Route53 records with routing policies other than 'simple')
type: string
targets:
description: The targets the DNS record points to

View File

@@ -5,3 +5,14 @@
App version: {{ .Chart.AppVersion }}
Image tag: {{ include "external-dns.image" . }}
***********************************************************************
{{- if eq (typeOf .Values.provider) "string" }}
🚧 DEPRECATIONS 🚧
The following features, functions, or methods are deprecated and no longer recommended for use.
{{/* The deprecation message for legacy 'provider: name'. */}}
{{- if eq (typeOf .Values.provider) "string" -}}
❗❗❗ DEPRECATED ❗❗❗ The legacy 'provider: <name>' configuration is in use. Support will be removed in future releases.
{{- end -}}
{{- end }}

View File

@@ -73,6 +73,7 @@ The image to use
{{/*
Provider name, Keeps backward compatibility on provider
TODO: line eq (typeOf .Values.provider) "string" to be removed in future releases
*/}}
{{- define "external-dns.providerName" -}}
{{- if eq (typeOf .Values.provider) "string" }}
@@ -93,3 +94,21 @@ The image to use for optional webhook sidecar
{{- printf "%s:%s" .repository .tag }}
{{- end }}
{{- end }}
{{/*
The pod affinity default label Selector
*/}}
{{- define "external-dns.labelSelector" -}}
labelSelector:
matchLabels:
{{ include "external-dns.selectorLabels" . | nindent 4 }}
{{- end }}
{{/*
Check if any Gateway API sources are enabled
*/}}
{{- define "external-dns.hasGatewaySources" -}}
{{- if or (has "gateway-httproute" .Values.sources) (has "gateway-grpcroute" .Values.sources) (has "gateway-tlsroute" .Values.sources) (has "gateway-tcproute" .Values.sources) (has "gateway-udproute" .Values.sources) -}}
true
{{- end -}}
{{- end }}

View File

@@ -18,10 +18,15 @@ rules:
{{- end }}
{{- if or (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "istio-gateway" .Values.sources) (has "istio-virtualservice" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
- apiGroups: [""]
resources: ["services","endpoints"]
resources: ["services"]
verbs: ["get","watch","list"]
{{- end }}
{{- if or (has "ingress" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
{{- if has "service" .Values.sources }}
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get","watch","list"]
{{- end }}
{{- if or (has "ingress" .Values.sources) (has "istio-gateway" .Values.sources) (has "istio-virtualservice" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) (has "gloo-proxy" .Values.sources) }}
- apiGroups: ["extensions","networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
@@ -55,13 +60,17 @@ rules:
resources: ["dnsendpoints/status"]
verbs: ["*"]
{{- end }}
{{- if or (has "gateway-httproute" .Values.sources) (has "gateway-grpcroute" .Values.sources) (has "gateway-tlsroute" .Values.sources) (has "gateway-tcproute" .Values.sources) (has "gateway-udproute" .Values.sources) }}
{{- if include "external-dns.hasGatewaySources" . }}
{{- if or (not .Values.namespaced) (and .Values.namespaced (not .Values.gatewayNamespace)) }}
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gateways"]
verbs: ["get","watch","list"]
{{- end }}
{{- if not .Values.namespaced }}
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get","watch","list"]
verbs: ["get","watch","list"]
{{- end }}
{{- end }}
{{- if has "gateway-httproute" .Values.sources }}
- apiGroups: ["gateway.networking.k8s.io"]
@@ -90,7 +99,7 @@ rules:
{{- end }}
{{- if has "gloo-proxy" .Values.sources }}
- apiGroups: ["gloo.solo.io","gateway.solo.io"]
resources: ["proxies","virtualservices"]
resources: ["proxies","virtualservices","gateways"]
verbs: ["get","watch","list"]
{{- end }}
{{- if has "kong-tcpingress" .Values.sources }}
@@ -116,12 +125,39 @@ rules:
resources: ["routegroups/status"]
verbs: ["patch","update"]
{{- end }}
{{- if has "f5-virtualserver" .Values.sources }}
{{- if or (has "f5-virtualserver" .Values.sources) (has "f5-transportserver" .Values.sources) }}
- apiGroups: ["cis.f5.com"]
resources: ["virtualservers"]
resources: ["virtualservers", "transportservers"]
verbs: ["get","watch","list"]
{{- end }}
{{- with .Values.rbac.additionalPermissions }}
{{- toYaml . | nindent 2 }}
{{- end }}
{{- if and .Values.rbac.create .Values.namespaced (include "external-dns.hasGatewaySources" .) }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ template "external-dns.fullname" . }}-namespaces
labels:
{{- include "external-dns.labels" . | nindent 4 }}
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get","watch","list"]
{{- if .Values.gatewayNamespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ template "external-dns.fullname" . }}-gateway
namespace: {{ .Values.gatewayNamespace }}
labels:
{{- include "external-dns.labels" . | nindent 4 }}
rules:
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gateways"]
verbs: ["get","watch","list"]
{{- end }}
{{- end }}
{{- end }}

View File

@@ -13,4 +13,39 @@ subjects:
- kind: ServiceAccount
name: {{ template "external-dns.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{- if and .Values.rbac.create .Values.namespaced (include "external-dns.hasGatewaySources" .) }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "external-dns.fullname" . }}-namespaces
labels:
{{- include "external-dns.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ template "external-dns.fullname" . }}-namespaces
subjects:
- kind: ServiceAccount
name: {{ template "external-dns.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{- if .Values.gatewayNamespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ template "external-dns.fullname" . }}-gateway
namespace: {{ .Values.gatewayNamespace }}
labels:
{{- include "external-dns.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ template "external-dns.fullname" . }}-gateway
subjects:
- kind: ServiceAccount
name: {{ template "external-dns.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -1,3 +1,4 @@
{{- $defaultSelector := (include "external-dns.labelSelector" $ ) | fromYaml -}}
{{- $providerName := tpl (include "external-dns.providerName" .) $ }}
apiVersion: apps/v1
kind: Deployment
@@ -40,7 +41,7 @@ spec:
{{- if not (quote .Values.automountServiceAccountToken | empty) }}
automountServiceAccountToken: {{ .Values.automountServiceAccountToken }}
{{- end }}
{{- with .Values.imagePullSecrets }}
{{- with (default .Values.global.imagePullSecrets .Values.imagePullSecrets) }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -99,25 +100,59 @@ spec:
{{- if .Values.txtOwnerId }}
- --txt-owner-id={{ .Values.txtOwnerId }}
{{- end }}
{{- if and .Values.txtPrefix .Values.txtSuffix }}
{{- fail (printf "'txtPrefix' and 'txtSuffix' are mutually exclusive") }}
{{- end }}
{{- if .Values.txtPrefix }}
- --txt-prefix={{ .Values.txtPrefix }}
{{- end }}
{{- if and (eq .Values.txtPrefix "") (ne .Values.txtSuffix "") }}
{{- else if .Values.txtSuffix }}
- --txt-suffix={{ .Values.txtSuffix }}
{{- end }}
{{- if .Values.namespaced }}
- --namespace={{ .Release.Namespace }}
{{- end }}
{{- if .Values.gatewayNamespace }}
- --gateway-namespace={{ .Values.gatewayNamespace }}
{{- end }}
{{- range .Values.domainFilters }}
- --domain-filter={{ . }}
{{- end }}
{{- range .Values.excludeDomains }}
- --exclude-domains={{ . }}
{{- end }}
{{- if .Values.labelFilter }}
- --label-filter={{ .Values.labelFilter }}
{{- end }}
{{- if .Values.annotationFilter }}
- --annotation-filter={{ .Values.annotationFilter }}
{{- end }}
{{- if .Values.annotationPrefix }}
- --annotation-prefix={{ .Values.annotationPrefix }}
{{- end }}
{{- range .Values.managedRecordTypes }}
- --managed-record-types={{ . }}
{{- end }}
- --provider={{ $providerName }}
{{- range .Values.extraArgs }}
{{- if kindIs "map" .Values.extraArgs }}
{{- range $key, $value := .Values.extraArgs }}
{{- if not (kindIs "invalid" $value) }}
{{- if kindIs "slice" $value }}
{{- range $value }}
- --{{ $key }}={{ tpl (. | toString) $ }}
{{- end }}
{{- else }}
- --{{ $key }}={{ tpl ($value | toString) $ }}
{{- end }}
{{- else }}
- --{{ $key }}
{{- end }}
{{- end }}
{{- end }}
{{- if kindIs "slice" .Values.extraArgs }}
{{- range .Values.extraArgs }}
- {{ tpl . $ }}
{{- end }}
{{- end }}
{{- end }}
ports:
- name: http
protocol: TCP
@@ -197,11 +232,71 @@ spec:
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- with .nodeAffinity }}
nodeAffinity:
{{- toYaml . | nindent 10 }}
{{- end }}
{{- with .podAffinity }}
podAffinity:
{{- with .preferredDuringSchedulingIgnoredDuringExecution }}
preferredDuringSchedulingIgnoredDuringExecution:
{{- range . }}
- podAffinityTerm:
{{- if dig "podAffinityTerm" "labelSelector" nil . }}
{{- toYaml .podAffinityTerm | nindent 16 }}
{{- else }}
{{- (merge $defaultSelector .podAffinityTerm) | toYaml | nindent 16 }}
{{- end }}
weight: {{ .weight }}
{{- end }}
{{- end }}
{{- with .requiredDuringSchedulingIgnoredDuringExecution }}
requiredDuringSchedulingIgnoredDuringExecution:
{{- range . }}
{{- if dig "labelSelector" nil . }}
- {{ toYaml . | indent 16 | trim }}
{{- else }}
- {{ (merge $defaultSelector .) | toYaml | indent 16 | trim }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- with .podAntiAffinity }}
podAntiAffinity:
{{- with .preferredDuringSchedulingIgnoredDuringExecution }}
preferredDuringSchedulingIgnoredDuringExecution:
{{- range . }}
- podAffinityTerm:
{{- if dig "podAffinityTerm" "labelSelector" nil . }}
{{- toYaml .podAffinityTerm | nindent 16 }}
{{- else }}
{{- (merge $defaultSelector .podAffinityTerm) | toYaml | nindent 16 }}
{{- end }}
weight: {{ .weight }}
{{- end }}
{{- end }}
{{- with .requiredDuringSchedulingIgnoredDuringExecution }}
requiredDuringSchedulingIgnoredDuringExecution:
{{- range . }}
{{- if dig "labelSelector" nil . }}
- {{ toYaml . | indent 16 | trim }}
{{- else }}
- {{ (merge $defaultSelector .) | toYaml | indent 16 | trim }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- range . }}
- {{ toYaml . | nindent 10 | trim }}
{{- if not (hasKey . "labelSelector") }}
labelSelector:
matchLabels:
{{- include "external-dns.selectorLabels" $ | nindent 14 }}
{{- end }}
{{- end }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:

View File

@@ -11,7 +11,9 @@ metadata:
{{- end }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- range $k, $v := . }}
{{- printf "%s: %s" (toYaml (tpl $k $)) (toYaml (tpl $v $)) | nindent 4 }}
{{- end }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
{{- end }}

View File

@@ -1,54 +1,697 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"provider": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
"affinity": {
"description": "Affinity settings for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). If an explicit label selector is not provided for pod affinity or pod anti-affinity one will be created from the pod selector labels.",
"type": "object"
},
"annotationFilter": {
"description": "Filter resources queried for endpoints by annotation selector.",
"type": [
"string",
"null"
]
},
"annotationPrefix": {
"description": "Annotation prefix for external-dns annotations (useful for split horizon DNS with multiple instances).",
"type": [
"string",
"null"
]
},
"automountServiceAccountToken": {
"description": "Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `Pod`.",
"type": "boolean"
},
"commonLabels": {
"description": "Labels to add to all chart resources.",
"type": "object"
},
"deploymentAnnotations": {
"description": "Annotations to add to the `Deployment`.",
"type": "object"
},
"deploymentStrategy": {
"description": "[Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy).",
"type": "object",
"properties": {
"type": {
"default": "Recreate",
"type": "string",
"enum": [
"Recreate",
"RollingUpdate"
]
}
},
"additionalProperties": true
},
"dnsConfig": {
"description": "[DNS config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config) for the pod, if not set the default will be used.",
"type": [
"object",
"null"
]
},
"dnsPolicy": {
"description": "[DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod, if not set the default will be used.",
"type": [
"string",
"null"
]
},
"domainFilters": {
"description": "Limit possible target zones by domain suffixes.",
"type": "array"
},
"enabled": {
"description": "No effect - reserved for use in sub-charting",
"type": [
"boolean",
"null"
]
},
"env": {
"description": "[Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `external-dns` container.",
"type": "array"
},
"excludeDomains": {
"description": "Intentionally exclude domains from being managed.",
"type": "array"
},
"extraArgs": {
"type": "array",
"description": "Extra arguments to provide to _ExternalDNS_. An array or map can be used, with maps allowing for value overrides; maps also support slice values to use the same arg multiple times.",
"type": [
"array",
"null",
"object"
],
"uniqueItems": true,
"items": {
"type": "string"
}
},
"secretConfiguration": {
"$comment": "This value is DEPRECATED as secrets should be configured external to the chart and exposed to the container via extraVolumes & extraVolumeMounts.",
"extraContainers": {
"description": "Extra containers to add to the `Deployment`.",
"type": "array"
},
"extraVolumeMounts": {
"description": "Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `external-dns` container.",
"type": "array"
},
"extraVolumes": {
"description": "Extra [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) for the `Pod`.",
"type": "array"
},
"fullnameOverride": {
"description": "Override the full name of the chart.",
"type": [
"string",
"null"
]
},
"gatewayNamespace": {
"description": "_Gateway API_ gateway namespace to watch.",
"type": [
"string",
"null"
]
},
"global": {
"type": "object",
"properties": {
"imagePullSecrets": {
"description": "Global image pull secrets.",
"type": "array",
"items": {
"type": "object"
}
}
}
},
"image": {
"type": "object",
"properties": {
"pullPolicy": {
"description": "Image pull policy for the `external-dns` container.",
"type": "string",
"enum": [
"IfNotPresent",
"Always"
]
},
"repository": {
"description": "Image repository for the `external-dns` container.",
"type": "string"
},
"tag": {
"description": "Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"imagePullSecrets": {
"description": "Image pull secrets.",
"type": "array",
"items": {
"type": "object"
}
},
"initContainers": {
"description": "[Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) to add to the `Pod` definition.",
"type": "array"
},
"interval": {
"description": "Interval for DNS updates.",
"type": "string"
},
"labelFilter": {
"description": "Filter resources queried for endpoints by label selector.",
"type": [
"string",
"null"
]
},
"livenessProbe": {
"description": "[Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container.",
"type": "object",
"properties": {
"failureThreshold": {
"type": "integer"
},
"httpGet": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"port": {
"type": "string"
}
}
},
"initialDelaySeconds": {
"type": "integer"
},
"periodSeconds": {
"type": "integer"
},
"successThreshold": {
"type": "integer"
},
"timeoutSeconds": {
"type": "integer"
}
}
},
"logFormat": {
"description": "Log format.",
"default": "text",
"type": "string",
"enum": [
"text",
"json"
]
},
"logLevel": {
"description": "Log level.",
"default": "info",
"type": "string",
"enum": [
"panic",
"debug",
"info",
"warning",
"error",
"fatal"
]
},
"managedRecordTypes": {
"description": "Record types to manage (default: A, AAAA, CNAME)",
"type": [
"array",
"null"
],
"uniqueItems": true,
"items": {
"type": "string"
}
},
"nameOverride": {
"description": "Override the name of the chart.",
"type": [
"string",
"null"
]
},
"namespaced": {
"description": "if `true`, _ExternalDNS_ will run in a namespaced scope (`Role`` and `Rolebinding`` will be namespaced too).",
"type": "boolean"
},
"nodeSelector": {
"description": "Node labels to match for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).",
"type": "object"
},
"podAnnotations": {
"description": "Annotations to add to the `Pod`.",
"type": "object"
},
"podLabels": {
"description": "Labels to add to the `Pod`.",
"type": "object"
},
"podSecurityContext": {
"description": "[Pod security context](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core), this supports full customisation.",
"type": "object",
"properties": {
"fsGroup": {
"type": "integer"
},
"runAsNonRoot": {
"type": "boolean"
},
"seccompProfile": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
}
}
}
},
"policy": {
"description": "How DNS records are synchronized between sources and providers; available values are `create-only`, `sync`, \u0026 `upsert-only`.",
"default": "upsert-only",
"type": "string",
"enum": [
"create-only",
"sync",
"upsert-only"
]
},
"priorityClassName": {
"description": "Priority class name for the `Pod`.",
"type": [
"string",
"null"
]
},
"provider": {
"type": [
"object",
"string"
],
"properties": {
"name": {
"description": "_ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers).",
"type": "string"
},
"webhook": {
"type": "object",
"properties": {
"args": {
"description": "Extra arguments to provide for the `webhook` container.",
"type": "array"
},
"env": {
"description": "[Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `webhook` container.",
"type": "array"
},
"extraVolumeMounts": {
"description": "Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `webhook` container.",
"type": "array"
},
"image": {
"type": "object",
"properties": {
"pullPolicy": {
"description": "Image pull policy for the `webhook` container.",
"type": "string"
},
"repository": {
"description": "Image repository for the `webhook` container.",
"type": [
"string",
"null"
]
},
"tag": {
"description": "Image tag for the `webhook` container.",
"type": [
"string",
"null"
]
}
}
},
"limits": {
"type": "object",
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
}
},
"livenessProbe": {
"description": "[Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container.",
"type": "object",
"properties": {
"failureThreshold": {
"type": [
"integer",
"null"
]
},
"httpGet": {
"type": "object",
"properties": {
"path": {
"type": [
"string",
"null"
]
},
"port": {
"default": "string",
"type": [
"integer",
"string"
]
}
}
},
"initialDelaySeconds": {
"type": [
"integer",
"null"
]
},
"periodSeconds": {
"type": [
"integer",
"null"
]
},
"successThreshold": {
"type": [
"integer",
"null"
]
},
"timeoutSeconds": {
"type": [
"integer",
"null"
]
}
}
},
"readinessProbe": {
"description": "[Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `webhook` container.",
"type": "object",
"properties": {
"failureThreshold": {
"type": [
"integer",
"null"
]
},
"httpGet": {
"type": "object",
"properties": {
"path": {
"type": [
"string",
"null"
]
},
"port": {
"default": "string",
"type": [
"integer",
"string"
]
}
}
},
"initialDelaySeconds": {
"type": [
"integer",
"null"
]
},
"periodSeconds": {
"type": [
"integer",
"null"
]
},
"successThreshold": {
"type": [
"integer",
"null"
]
},
"timeoutSeconds": {
"type": [
"integer",
"null"
]
}
}
},
"requests": {
"type": "object",
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
}
},
"resources": {
"description": "[Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the `webhook` container.",
"type": "object"
},
"securityContext": {
"description": "[Pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `webhook` container.",
"type": "object"
},
"service": {
"type": "object",
"properties": {
"port": {
"description": "Webhook exposed HTTP port for the service.",
"type": "integer"
}
}
},
"serviceMonitor": {
"description": "Optional [Service Monitor](https://prometheus-operator.dev/docs/operator/design/#servicemonitor) configuration for the `webhook` container.",
"type": "object",
"properties": {
"bearerTokenFile": {
"type": [
"string",
"null"
]
},
"interval": {
"type": [
"string",
"null"
]
},
"metricRelabelings": {
"type": "array"
},
"relabelings": {
"type": "array"
},
"scheme": {
"type": [
"string",
"null"
]
},
"scrapeTimeout": {
"type": [
"string",
"null"
]
},
"tlsConfig": {
"type": "object"
}
}
}
}
}
}
},
"rbac": {
"type": "object",
"properties": {
"additionalPermissions": {
"description": "Additional rules to add to the `ClusterRole`.",
"type": "array"
},
"create": {
"description": "If `true`, create a `ClusterRole` \u0026 `ClusterRoleBinding` with access to the Kubernetes API.",
"type": "boolean"
}
},
"additionalProperties": true
},
"readinessProbe": {
"description": "[Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container.",
"type": "object",
"properties": {
"failureThreshold": {
"type": "integer"
},
"httpGet": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"port": {
"type": "string"
}
}
},
"initialDelaySeconds": {
"type": "integer"
},
"periodSeconds": {
"type": "integer"
},
"successThreshold": {
"type": "integer"
},
"timeoutSeconds": {
"type": "integer"
}
}
},
"registry": {
"description": "Specify the registry for storing ownership and labels. Valid values are `txt`, `aws-sd`, `dynamodb` \u0026 `noop`.",
"default": "txt",
"type": "string",
"enum": [
"txt",
"aws-sd",
"dynamodb",
"noop"
]
},
"resources": {
"description": "[Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the `external-dns` container.",
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
}
},
"requests": {
"type": "object",
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
}
}
}
},
"revisionHistoryLimit": {
"description": "Specify the number of old `ReplicaSets` to retain to allow rollback of the `Deployment``.",
"type": [
"integer",
"null"
],
"minimum": 0
},
"secretConfiguration": {
"type": "object",
"properties": {
"data": {
"description": "`Secret` data.",
"type": "object"
},
"enabled": {
"description": "If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**).",
"type": "boolean"
},
"mountPath": {
"description": "Mount path for the `Secret`, this can be templated.",
"type": [
"string",
"null"
]
},
"subPath": {
"description": "Sub-path for mounting the `Secret`, this can be templated.",
"type": [
"string",
"null"
]
}
}
},
"securityContext": {
"description": "[Security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `external-dns` container.",
"type": "object",
"properties": {
"allowPrivilegeEscalation": {
"type": "boolean"
},
"data": {
"capabilities": {
"type": "object",
"patternProperties": {
".+": {
"type": "string"
"properties": {
"drop": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"privileged": {
"type": "boolean"
},
"readOnlyRootFilesystem": {
"type": "boolean"
},
"runAsGroup": {
"type": "integer"
},
"runAsNonRoot": {
"type": "boolean"
},
"runAsUser": {
"type": "integer"
}
}
},
@@ -56,36 +699,194 @@
"type": "object",
"properties": {
"annotations": {
"description": "Service annotations.",
"type": "object"
},
"ipFamilies": {
"type": "array",
"description": "Service IP families (e.g. IPv4 and/or IPv6).",
"type": [
"array",
"null"
],
"maxItems": 2,
"minItems": 0,
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"IPv6",
"IPv4"
"IPv4",
"IPv6"
]
}
},
"ipFamilyPolicy": {
"description": "Service IP family policy.",
"type": [
"string",
"null"
],
"items": {
"type": "string",
"enum": [
"SingleStack",
"PreferDualStack",
"RequireDualStack"
]
}
"enum": [
"SingleStack",
"PreferDualStack",
"RequireDualStack",
null
]
},
"port": {
"type": "integer"
"description": "Service HTTP port.",
"default": 7979,
"type": "integer",
"minimum": 0
}
}
},
"serviceAccount": {
"type": "object",
"properties": {
"annotations": {
"description": "Annotations to add to the service account. Templates are allowed in both the key and the value. Example: `example.com/annotation/{{ .Values.nameOverride }}: {{ .Values.nameOverride }}`",
"type": "object"
},
"automountServiceAccountToken": {
"description": "Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `ServiceAccount`.",
"type": "boolean"
},
"create": {
"description": "If `true`, create a new `ServiceAccount`.",
"type": "boolean"
},
"labels": {
"description": "Labels to add to the service account.",
"type": "object"
},
"name": {
"description": "If this is set and `serviceAccount.create` is `true` this will be used for the created `ServiceAccount` name, if set and `serviceAccount.create` is `false` then this will define an existing `ServiceAccount` to use.",
"type": [
"string",
"null"
]
}
}
},
"serviceMonitor": {
"type": "object",
"properties": {
"additionalLabels": {
"description": "Additional labels for the `ServiceMonitor`.",
"type": "object"
},
"annotations": {
"description": "Annotations to add to the `ServiceMonitor`.",
"type": "object"
},
"bearerTokenFile": {
"description": "Provide a bearer token file for the `ServiceMonitor`.",
"type": [
"string",
"null"
]
},
"enabled": {
"description": "If `true`, create a `ServiceMonitor` resource to support the _Prometheus Operator_.",
"type": "boolean"
},
"interval": {
"description": "If set override the _Prometheus_ default interval.",
"type": [
"string",
"null"
]
},
"metricRelabelings": {
"description": "[Metric relabel configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs) to apply to samples before ingestion.",
"type": "array"
},
"namespace": {
"description": "If set create the `ServiceMonitor` in an alternate namespace.",
"type": [
"string",
"null"
]
},
"relabelings": {
"description": "[Relabel configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) to apply to samples before ingestion.",
"type": "array"
},
"scheme": {
"description": "If set overrides the _Prometheus_ default scheme.",
"type": [
"string",
"null"
]
},
"scrapeTimeout": {
"description": "If set override the _Prometheus_ default scrape timeout.",
"type": [
"string",
"null"
]
},
"targetLabels": {
"description": "Provide target labels for the `ServiceMonitor`.",
"type": "array"
},
"tlsConfig": {
"description": "Configure the `ServiceMonitor` [TLS config](https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#tlsconfig).",
"type": "object"
}
}
},
"shareProcessNamespace": {
"description": "If `true`, the `Pod` will have [process namespace sharing](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/) enabled.",
"type": "boolean"
},
"sources": {
"description": "_Kubernetes_ resources to monitor for DNS entries.",
"type": "array",
"items": {
"type": "string"
}
},
"terminationGracePeriodSeconds": {
"description": "Termination grace period for the `Pod` in seconds.",
"type": [
"integer",
"null"
]
},
"tolerations": {
"description": "Node taints which will be tolerated for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).",
"type": "array"
},
"topologySpreadConstraints": {
"description": "Topology spread constraints for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). If an explicit label selector is not provided one will be created from the pod selector labels.",
"type": "array"
},
"triggerLoopOnEvent": {
"description": "If `true`, triggers run loop on create/update/delete events in addition of regular interval.",
"type": "boolean"
},
"txtOwnerId": {
"description": "Specify an identifier for this instance of _ExternalDNS_ when using a registry other than `noop`.",
"type": [
"string",
"null"
]
},
"txtPrefix": {
"description": "Specify a prefix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtSuffix`.",
"type": [
"string",
"null"
]
},
"txtSuffix": {
"description": "Specify a suffix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtPrefix`.",
"type": [
"string",
"null"
]
}
}
},
"additionalProperties": true
}

View File

@@ -2,22 +2,26 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
global:
# -- Global image pull secrets.
imagePullSecrets: [] # @schema item: object
image: # @schema additionalProperties: false
# -- Image repository for the `external-dns` container.
repository: registry.k8s.io/external-dns/external-dns
# -- (string) Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set.
tag:
# -- Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set.
tag: # @schema type:[string, null]
# -- Image pull policy for the `external-dns` container.
pullPolicy: IfNotPresent
pullPolicy: IfNotPresent # @schema enum:[IfNotPresent, Always]
# -- Image pull secrets.
imagePullSecrets: []
imagePullSecrets: [] # @schema item: object
# -- (string) Override the name of the chart.
nameOverride:
nameOverride: # @schema type:[string, null]; default: null
# -- (string) Override the full name of the chart.
fullnameOverride:
fullnameOverride: # @schema type:[string, null]; default: null
# -- Labels to add to all chart resources.
commonLabels: {}
@@ -27,24 +31,26 @@ serviceAccount:
create: true
# -- Labels to add to the service account.
labels: {}
# -- Annotations to add to the service account.
# -- Annotations to add to the service account. Templates are allowed in both the key and the value. Example: `example.com/annotation/{{ .Values.nameOverride }}: {{ .Values.nameOverride }}`
annotations: {}
# -- (string) If this is set and `serviceAccount.create` is `true` this will be used for the created `ServiceAccount` name, if set and `serviceAccount.create` is `false` then this will define an existing `ServiceAccount` to use.
name:
name: # @schema type:[string, null]; default: null
# -- Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `ServiceAccount`.
automountServiceAccountToken:
automountServiceAccountToken: true
service:
# -- Service annotations.
annotations: {}
# -- Service HTTP port.
port: 7979
# -- Service IP families.
ipFamilies: []
# -- (string) Service IP family policy.
ipFamilyPolicy:
port: 7979 # @schema minimum:0; default:7979
# -- Service IP families (e.g. IPv4 and/or IPv6).
ipFamilies: [] # @schema type: [array, null]; item: string; itemEnum: ["IPv4", "IPv6"]; minItems:0; maxItems:2; uniqueItems: true
# - IPv4
# - IPv6
# -- Service IP family policy.
ipFamilyPolicy: # @schema type: [string, null]; enum:[SingleStack, PreferDualStack, RequireDualStack, null]
rbac:
rbac: # @schema additionalProperties: true
# -- If `true`, create a `ClusterRole` & `ClusterRoleBinding` with access to the Kubernetes API.
create: true
# -- Additional rules to add to the `ClusterRole`.
@@ -54,14 +60,14 @@ rbac:
deploymentAnnotations: {}
# -- Extra containers to add to the `Deployment`.
extraContainers: {}
extraContainers: []
# -- [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy).
deploymentStrategy:
type: Recreate
deploymentStrategy: # @schema additionalProperties: true
type: Recreate # @schema enum:[Recreate, RollingUpdate]; type:string; default: Recreate
# -- (int) Specify the number of old `ReplicaSets` to retain to allow rollback of the `Deployment``.
revisionHistoryLimit:
revisionHistoryLimit: # @schema type:[integer, null];minimum:0
# -- Labels to add to the `Pod`.
podLabels: {}
@@ -70,7 +76,7 @@ podLabels: {}
podAnnotations: {}
# -- (bool) Set this to `false` to [opt out of API credential automounting](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#opt-out-of-api-credential-automounting) for the `Pod`.
automountServiceAccountToken:
automountServiceAccountToken: true
# -- If `true`, the `Pod` will have [process namespace sharing](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/) enabled.
shareProcessNamespace: false
@@ -84,16 +90,16 @@ podSecurityContext:
type: RuntimeDefault
# -- (string) Priority class name for the `Pod`.
priorityClassName:
priorityClassName: # @schema type:[string, null]; default: null
# -- (int) Termination grace period for the `Pod` in seconds.
terminationGracePeriodSeconds:
terminationGracePeriodSeconds: # @schema type:[integer, null]
# -- (string) [DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod, if not set the default will be used.
dnsPolicy:
dnsPolicy: # @schema type:[string, null]; default: null
# -- (object) [DNS config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config) for the pod, if not set the default will be used.
dnsConfig:
dnsConfig: # @schema type:[object, null]; default: null
# -- [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) to add to the `Pod` definition.
initContainers: []
@@ -166,17 +172,17 @@ serviceMonitor:
# -- Annotations to add to the `ServiceMonitor`.
annotations: {}
# -- (string) If set create the `ServiceMonitor` in an alternate namespace.
namespace:
namespace: # @schema type:[string, null]; default: null
# -- (string) If set override the _Prometheus_ default interval.
interval:
interval: # @schema type:[string, null]; default: null
# -- (string) If set override the _Prometheus_ default scrape timeout.
scrapeTimeout:
scrapeTimeout: # @schema type:[string, null]; default: null
# -- (string) If set overrides the _Prometheus_ default scheme.
scheme:
scheme: # @schema type:[string, null]; default: null
# -- Configure the `ServiceMonitor` [TLS config](https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#tlsconfig).
tlsConfig: {}
# -- (string) Provide a bearer token file for the `ServiceMonitor`.
bearerTokenFile:
bearerTokenFile: # @schema type:[string, null]; default: null
# -- [Relabel configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) to apply to samples before ingestion.
relabelings: []
# -- [Metric relabel configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs) to apply to samples before ingestion.
@@ -185,10 +191,10 @@ serviceMonitor:
targetLabels: []
# -- Log level.
logLevel: info
logLevel: info # @schema enum:[panic, debug, info, warning, error, fatal]; type:string; default: "info"
# -- Log format.
logFormat: text
logFormat: text # @schema enum:["text", "json"]; type:string; default: "text"
# -- Interval for DNS updates.
interval: 1m
@@ -199,41 +205,56 @@ triggerLoopOnEvent: false
# -- if `true`, _ExternalDNS_ will run in a namespaced scope (`Role`` and `Rolebinding`` will be namespaced too).
namespaced: false
# -- _Gateway API_ gateway namespace to watch.
gatewayNamespace: # @schema type:[string, null]; default: null
# -- _Kubernetes_ resources to monitor for DNS entries.
sources:
- service
- ingress
# -- How DNS records are synchronized between sources and providers; available values are `sync` & `upsert-only`.
policy: upsert-only
# -- How DNS records are synchronized between sources and providers; available values are `create-only`, `sync`, & `upsert-only`.
policy: upsert-only # @schema enum:[create-only, sync, upsert-only]; type:string; default: "upsert-only"
# -- Specify the registry for storing ownership and labels.
# Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`.
registry: txt
# -- (string) Specify an identifier for this instance of _ExternalDNS_ wWhen using a registry other than `noop`.
txtOwnerId:
registry: txt # @schema enum:[txt, aws-sd, dynamodb, noop]; default: "txt"
# -- (string) Specify an identifier for this instance of _ExternalDNS_ when using a registry other than `noop`.
txtOwnerId: # @schema type:[string, null]; default: null
# -- (string) Specify a prefix for the domain names of TXT records created for the `txt` registry.
# Mutually exclusive with `txtSuffix`.
txtPrefix:
txtPrefix: # @schema type:[string, null]; default: null
# -- (string) Specify a suffix for the domain names of TXT records created for the `txt` registry.
# Mutually exclusive with `txtPrefix`.
txtSuffix:
txtSuffix: # @schema type:[string, null]; default: null
## - Limit possible target zones by domain suffixes.
# -- Limit possible target zones by domain suffixes.
domainFilters: []
## -- Intentionally exclude domains from being managed.
# -- Intentionally exclude domains from being managed.
excludeDomains: []
provider:
# -- Filter resources queried for endpoints by label selector.
labelFilter: # @schema type: [string,null]; default: null
# -- Filter resources queried for endpoints by annotation selector.
annotationFilter: # @schema type: [string,null]; default: null
# -- Annotation prefix for external-dns annotations (useful for split horizon DNS with multiple instances).
annotationPrefix: # @schema type: [string,null]; default: null
# -- Record types to manage (default: A, AAAA, CNAME)
managedRecordTypes: [] # @schema type: [array, null]; item: string; uniqueItems: true
provider: # @schema type: [object, string]
# -- _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers).
name: aws
webhook:
image:
# -- (string) Image repository for the `webhook` container.
repository:
repository: # @schema type:[string, null]; default: null
# -- (string) Image tag for the `webhook` container.
tag:
tag: # @schema type:[string, null]; default: null
# -- Image pull policy for the `webhook` container.
pullPolicy: IfNotPresent
# -- [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `webhook` container.
@@ -251,47 +272,51 @@ provider:
# @default -- See _values.yaml_
livenessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2
successThreshold: 1
path: /healthz # @schema type:[string, null]; default: null
port: http-webhook # @schema type:[integer,string]; default: string
initialDelaySeconds: 10 # @schema type:[integer, null]; default: null
periodSeconds: 10 # @schema type:[integer, null]; default: null
timeoutSeconds: 5 # @schema type:[integer, null]; default: null
failureThreshold: 2 # @schema type:[integer, null]; default: null
successThreshold: 1 # @schema type:[integer, null]; default: null
# -- [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `webhook` container.
# @default -- See _values.yaml_
readinessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
path: /healthz # @schema type:[string, null]; default: null
port: http-webhook # @schema type:[integer,string]; default: string
initialDelaySeconds: 5 # @schema type:[integer, null]; default: null
periodSeconds: 10 # @schema type:[integer, null]; default: null
timeoutSeconds: 5 # @schema type:[integer, null]; default: null
failureThreshold: 6 # @schema type:[integer, null]; default: null
successThreshold: 1 # @schema type:[integer, null]; default: null
service:
# -- Webhook exposed HTTP port for the service.
port: 8080
# -- Optional [Service Monitor](https://prometheus-operator.dev/docs/operator/design/#servicemonitor) configuration for the `webhook` container.
# @default -- See _values.yaml_
serviceMonitor:
interval:
scheme:
interval: # @schema type:[string, null]; default: null
scheme: # @schema type:[string, null]; default: null
tlsConfig: {}
bearerTokenFile:
scrapeTimeout:
bearerTokenFile: # @schema type:[string, null]; default: null
scrapeTimeout: # @schema type:[string, null]; default: null
metricRelabelings: []
relabelings: []
# -- Extra arguments to provide to _ExternalDNS_.
extraArgs: []
# An array or map can be used, with maps allowing for value overrides; maps also support slice values to use the same arg multiple times.
extraArgs: {} # @schema type: [array, null, object]; item: string; uniqueItems: true
secretConfiguration:
# -- If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**).
enabled: false
# -- Mount path for the `Secret`, this can be templated.
mountPath:
mountPath: # @schema type:[string, null]; default: null
# -- Sub-path for mounting the `Secret`, this can be templated.
subPath:
subPath: # @schema type:[string, null]; default: null
# -- `Secret` data.
data: {}
# -- (bool) No effect - reserved for use in sub-charting.
enabled: # @schema type: [boolean, null]; description: No effect - reserved for use in sub-charting

View File

@@ -37,6 +37,10 @@ spec:
displayHtmlName: {{ $brandingConfig.branding }}
{{- end }}
{{- end }}
sessions:
ssoSessionSettings:
idleTimeout: 86400
maxLifespan: 604800
---

View File

@@ -4,6 +4,17 @@ export NAMESPACE=cozy-keycloak
include ../../../hack/common-envs.mk
include ../../../hack/package.mk
image:
docker buildx build images/keycloak-operator \
--tag $(REGISTRY)/keycloak-operator:$(call settag,$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/keycloak-operator:latest \
--cache-to type=inline \
--metadata-file images/keycloak-operator.json \
$(BUILDX_ARGS)
TAG="$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/keycloak-operator.json -r)" \
yq -i '.keycloak-operator.image.tag = strenv(TAG)' values.yaml
rm -f images/keycloak-operator.json
update:
rm -rf charts
helm repo add epamedp https://epam.github.io/edp-helm-charts/stable

View File

@@ -157,30 +157,29 @@ annotations:
- apiVersion: v1.edp.epam.com/v1
kind: KeycloakRealmIdentityProvider
metadata:
name: instagram-test
name: github-test
spec:
realm: d2-id-k8s-realm-name
alias: instagram
alias: github
authenticateByDefault: false
enabled: true
firstBrokerLoginFlowAlias: "first broker login"
providerId: "instagram"
providerId: "github"
config:
clientId: "foo"
clientSecret: "bar"
hideOnLoginPage: "true"
syncMode: "IMPORT"
useJwksUrl: "true"
mappers:
- name: "test3212"
identityProviderMapper: "oidc-hardcoded-role-idp-mapper"
identityProviderAlias: "instagram"
identityProviderAlias: "github"
config:
role: "role-tr"
syncMode: "INHERIT"
- name: "test-33221"
identityProviderMapper: "hardcoded-attribute-idp-mapper"
identityProviderAlias: "instagram"
identityProviderAlias: "github"
config:
attribute: "foo"
"attribute.value": "bar"
@@ -272,8 +271,8 @@ annotations:
secret: secret-name-in-operator-ns
url: https://keycloak.example.com
artifacthub.io/images: |
- name: keycloak-operator:1.25.0
image: epamedp/keycloak-operator:1.25.0
- name: keycloak-operator:1.32.0
image: epamedp/keycloak-operator:1.32.0
artifacthub.io/license: Apache-2.0
artifacthub.io/links: |
- name: KubeRocketCI Documentation
@@ -283,7 +282,7 @@ annotations:
artifacthub.io/operator: "true"
artifacthub.io/operatorCapabilities: Deep Insights
apiVersion: v2
appVersion: 1.25.0
appVersion: 1.32.0
description: A Helm chart for KubeRocketCI Keycloak Operator
home: https://docs.kuberocketci.io/
icon: https://docs.kuberocketci.io/img/logo.svg
@@ -308,4 +307,4 @@ name: keycloak-operator
sources:
- https://github.com/epam/edp-keycloak-operator
type: application
version: 1.25.0
version: 1.32.0

View File

@@ -1,6 +1,6 @@
# keycloak-operator
![Version: 1.25.0](https://img.shields.io/badge/Version-1.25.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.25.0](https://img.shields.io/badge/AppVersion-1.25.0-informational?style=flat-square)
![Version: 1.32.0](https://img.shields.io/badge/Version-1.32.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.32.0](https://img.shields.io/badge/AppVersion-1.32.0-informational?style=flat-square)
A Helm chart for KubeRocketCI Keycloak Operator
@@ -16,6 +16,7 @@ _**NOTE:** Operator is platform-independent, which is why there is a unified ins
1. Linux machine or Windows Subsystem for Linux instance with [Helm 3](https://helm.sh/docs/intro/install/) installed;
2. Cluster admin access to the cluster;
3. [cert-manager](https://cert-manager.io/docs/installation/) installed in the cluster (required for webhook functionality, can be disabled via `enableWebhooks: false`);
## Installation Using Helm Chart
@@ -32,7 +33,7 @@ To install the Keycloak Operator, follow the steps below:
```bash
helm search repo epamedp/keycloak-operator -l
NAME CHART VERSION APP VERSION DESCRIPTION
epamedp/keycloak-operator 1.24.0 1.24.0 A Helm chart for KRCI Keycloak Operator
epamedp/keycloak-operator 1.31.0 1.31.0 A Helm chart for KRCI Keycloak Operator
```
_**NOTE:** It is highly recommended to use the latest stable version._
@@ -129,14 +130,21 @@ Development versions are also available from the [snapshot helm chart repository
|-----|------|---------|-------------|
| affinity | object | `{}` | Affinity for pod assignment |
| annotations | object | `{}` | Annotations to be added to the Deployment |
| clusterDomain | string | `"cluster.local"` | Cluster domain for constructing service DNS names |
| clusterReconciliationEnabled | bool | `false` | If clusterReconciliationEnabled is true, the operator reconciles all Keycloak instances in the cluster; otherwise, it only reconciles instances in the same namespace by default, and cluster-scoped resources are ignored. |
| containerSecurityContext | object | `{"allowPrivilegeEscalation":false}` | Container Security Context Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ |
| enableOwnerRef | bool | `true` | If set to true, the operator will set the owner reference for all resources that have Keycloak or KeycloakRealm as reference. This is legacy behavior and not recommended for use. In the future, this will be set to false by default. |
| enableWebhooks | bool | `true` | If set to true, enables webhook resources (ValidatingWebhookConfiguration, Service, and Certificate). Webhooks require cert-manager to be installed in the cluster. |
| extraVolumeMounts | list | `[]` | Additional volumeMounts to be added to the container |
| extraVolumes | list | `[]` | Additional volumes to be added to the pod |
| image.registry | string | `""` | KubeRocketCI keycloak-operator Docker image registry. |
| image.repository | string | `"epamedp/keycloak-operator"` | KubeRocketCI keycloak-operator Docker image name. The released image can be found on [Dockerhub](https://hub.docker.com/r/epamedp/keycloak-operator) |
| image.tag | string | `nil` | KubeRocketCI keycloak-operator Docker image tag. The released image can be found on [Dockerhub](https://hub.docker.com/r/epamedp/keycloak-operator/tags) |
| imagePullPolicy | string | `"IfNotPresent"` | If defined, a imagePullPolicy applied to the deployment |
| imagePullSecrets | list | `[]` | If defined, imagePullSecrets are applied to deployment |
| name | string | `"keycloak-operator"` | Application name string |
| nodeSelector | object | `{}` | Node labels for pod assignment |
| podLabels | object | `{}` | Labels to be added to the pod |
| resources | object | `{"limits":{"memory":"192Mi"},"requests":{"cpu":"50m","memory":"64Mi"}}` | Resource limits and requests for the pod |
| securityContext | object | `{"runAsNonRoot":true}` | Deployment Security Context Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ |
| tolerations | list | `[]` | Node tolerations for server scheduling to nodes with taints |

View File

@@ -17,6 +17,7 @@ _**NOTE:** Operator is platform-independent, which is why there is a unified ins
1. Linux machine or Windows Subsystem for Linux instance with [Helm 3](https://helm.sh/docs/intro/install/) installed;
2. Cluster admin access to the cluster;
3. [cert-manager](https://cert-manager.io/docs/installation/) installed in the cluster (required for webhook functionality, can be disabled via `enableWebhooks: false`);
## Installation Using Helm Chart
@@ -33,7 +34,7 @@ To install the Keycloak Operator, follow the steps below:
```bash
helm search repo epamedp/keycloak-operator -l
NAME CHART VERSION APP VERSION DESCRIPTION
epamedp/keycloak-operator 1.24.0 1.24.0 A Helm chart for KRCI Keycloak Operator
epamedp/keycloak-operator 1.31.0 1.31.0 A Helm chart for KRCI Keycloak Operator
```
_**NOTE:** It is highly recommended to use the latest stable version._

View File

@@ -7,3 +7,25 @@ spec:
realmName: realm-sample1234
authenticationFlows:
browserFlow: browserFlow-sample
login:
userRegistration: true
forgotPassword: true
rememberMe: true
emailAsUsername: false
loginWithEmail: true
duplicateEmails: false
verifyEmail: true
editUsername: false
sessions:
ssoLoginSettings:
accessCodeLifespanLogin: 1800 # 30 minutes
accessCodeLifespanUserAction: 300 # 5 minutes
ssoSessionSettings:
idleTimeout: 1800 # 30 minutes
idleTimeoutRememberMe: 604800 # 7 days
maxLifespan: 36000 # 10 hours
maxLifespanRememberMe: 2592000 # 30 days
ssoOfflineSessionSettings:
idleTimeout: 2592000 # 30 days
maxLifespan: 5184000 # 60 days
maxLifespanEnabled: true

View File

@@ -14,11 +14,16 @@ spec:
webUrl: https://argocd.example.com
adminUrl: https://admin.example.com
homeUrl: /home/
defaultClientScopes:
- groups
redirectUris:
- /url1/*
- /url2/*
clientRolesV2:
- name: roleA
description: "Role A"
associatedClientRoles:
- roleB
- name: roleB
description: "Role B"
---

View File

@@ -8,6 +8,7 @@ spec:
name: keycloakrealm-sample
kind: KeycloakRealm
description: "Group Membership"
type: default
protocol: openid-connect
protocolMappers:
- name: groups

View File

@@ -0,0 +1,45 @@
# Organization with identity provider configuration
apiVersion: v1.edp.epam.com/v1alpha1
kind: KeycloakOrganization
metadata:
name: test-keycloak-organization
namespace: default
spec:
name: "Test Organization"
alias: "test-org"
domains:
- "example.com"
- "test.com"
redirectUrl: "https://example.com/redirect"
description: "Test organization"
attributes:
department:
- "engineering"
- "qa"
location:
- "us-east"
identityProviders:
- alias: "test-org-idp"
realmRef:
kind: KeycloakRealm
name: test-org-realm
---
apiVersion: v1.edp.epam.com/v1
kind: KeycloakRealmIdentityProvider
metadata:
name: test-org-idp
namespace: default
spec:
alias: "test-org-idp"
enabled: true
providerId: "github"
realmRef:
kind: KeycloakRealm
name: test-org-realm
config:
clientId: "test-org-client-id"
clientSecret: "test-org-client-secret"
kc.org.domain: "example.com"
kc.org.broker.redirect.mode.email-matches: "true"

View File

@@ -3,7 +3,6 @@ kind: KeycloakRealm
metadata:
name: keycloakrealm-sample
spec:
id: bfebeff6-ac63-4b46-a1f3-37df5099a9c4
realmName: realm-sample
keycloakRef:
name: keycloak-sample
@@ -16,6 +15,7 @@ spec:
realmEventConfig:
adminEventsDetailsEnabled: false
adminEventsEnabled: true
adminEventsExpiration: 544
enabledEventTypes:
- UPDATE_CONSENT_ERROR
- CLIENT_LOGIN
@@ -32,6 +32,15 @@ spec:
refreshTokenMaxReuse: 300
revokeRefreshToken: true
defaultSignatureAlgorithm: RS256
login:
userRegistration: true
forgotPassword: true
rememberMe: true
emailAsUsername: true
loginWithEmail: true
duplicateEmails: false
verifyEmail: true
editUsername: true
userProfileConfig:
unmanagedAttributePolicy: "ENABLED"
attributes:
@@ -94,3 +103,16 @@ spec:
key: "password"
username:
value: "username"
sessions:
ssoLoginSettings:
accessCodeLifespanLogin: 1800 # 30 minutes
accessCodeLifespanUserAction: 300 # 5 minutes
ssoSessionSettings:
idleTimeout: 1800 # 30 minutes
idleTimeoutRememberMe: 604800 # 7 days
maxLifespan: 36000 # 10 hours
maxLifespanRememberMe: 2592000 # 30 days
ssoOfflineSessionSettings:
idleTimeout: 2592000 # 30 days
maxLifespan: 5184000 # 60 days
maxLifespanEnabled: true

View File

@@ -7,3 +7,29 @@ spec:
name: keycloakrealm-sample
kind: KeycloakRealm
name: ArgoCDAdmins
---
# Example of a child group using parentGroup
apiVersion: v1.edp.epam.com/v1
kind: KeycloakRealmGroup
metadata:
name: keycloakrealmgroup-child-sample
spec:
realmRef:
name: keycloakrealm-sample
kind: KeycloakRealm
name: ArgoCDDevelopers
parentGroup:
name: keycloakrealmgroup-sample
---
apiVersion: v1.edp.epam.com/v1
kind: KeycloakRealmGroup
metadata:
name: keycloakrealmgroup-child-sample2
spec:
realmRef:
name: keycloakrealm-sample
kind: KeycloakRealm
name: ArgoCDGoDevs
parentGroup:
name: keycloakrealmgroup-child-sample

View File

@@ -5,23 +5,33 @@ metadata:
spec:
realmRef:
kind: KeycloakRealm
name: realm
alias: instagram
name: keycloakrealm-sample
alias: github
authenticateByDefault: false
enabled: true
firstBrokerLoginFlowAlias: "first broker login"
providerId: "instagram"
postBrokerLoginFlowAlias: "browser"
providerId: "github"
config:
clientId: "foo"
clientSecret: "$secretName:secretKey"
hideOnLoginPage: "true"
clientSecret: "$test-idp-secret:secret"
syncMode: "IMPORT"
useJwksUrl: "true"
mappers:
- name: "test-33221"
identityProviderMapper: "hardcoded-attribute-idp-mapper"
identityProviderAlias: "instagram"
identityProviderAlias: "github"
config:
attribute: "foo"
"attribute.value": "bar"
syncMode: "IMPORT"
---
apiVersion: v1
kind: Secret
metadata:
name: test-idp-secret
type: Opaque
data:
secret: "c2VjcmV0" # base64 encoded value of "secret"

View File

@@ -13,8 +13,21 @@ spec:
enabled: true
emailVerified: true
keepResource: true
passwordSecret:
key: password
name: keycloakrealmuser-sample-password
temporary: true
requiredUserActions:
- UPDATE_PASSWORD
attributes:
foo: "bar"
baz: "jazz"
- UPDATE_PROFILE
attributesV2:
department: ["IT"]
location: ["Winterfell"]
---
apiVersion: v1
kind: Secret
metadata:
name: keycloakrealmuser-sample-password
type: Opaque
data:
password: "U29tZVBhc3N3b3JkMTIzIQ==" # SomePassword123!

View File

@@ -19,3 +19,5 @@ spec:
passwordSecret:
name: existing-k8s-secret
key: key-which-contains-password
identityProviders:
- provider-alias

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: clusterkeycloakrealms.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -97,6 +97,57 @@ spec:
nullable: true
type: boolean
type: object
login:
description: Login settings for the realm.
nullable: true
properties:
duplicateEmails:
default: false
description: DuplicateEmails allows multiple users to have the
same email address.
type: boolean
editUsername:
default: false
description: EditUsername allows to edit username.
type: boolean
emailAsUsername:
default: false
description: EmailAsUsername allows users to set email as username.
type: boolean
forgotPassword:
default: false
description: ForgotPassword shows a link on the login page for
users who have forgotten their credentials.
type: boolean
loginWithEmail:
default: true
description: LoginWithEmail allows users to log in with their
email address.
type: boolean
rememberMe:
default: false
description: RememberMe shows checkbox on the login page to allow
the user to remain logged in between browser restarts until
the session expires.
type: boolean
userRegistration:
default: false
description: UserRegistration enables/disables the registration
page. A link for registration will show on the login page too.
type: boolean
verifyEmail:
default: false
description: VerifyEmail requires user to verify their email address
after initial login or after address changes are submitted.
type: boolean
type: object
organizationsEnabled:
default: false
description: |-
OrganizationsEnabled enables Keycloak Organizations feature for this realm.
When enabled, this realm can support Organization resources for multi-tenant scenarios,
identity provider groupings, and domain-based user routing.
type: boolean
passwordPolicy:
description: PasswordPolicies is a list of password policies to apply
to the realm.
@@ -153,6 +204,80 @@ spec:
realmName:
description: RealmName specifies the name of the realm.
type: string
sessions:
description: Sessions defines the session settings for the realm.
properties:
ssoLoginSettings:
description: SSOLoginSettings defines the SSO login settings for
the realm.
properties:
accessCodeLifespanLogin:
default: 1800
description: AccessCodeLifespanLogin represents the max time
a user has to complete a login. This is recommended to be
relatively long, such as 30 minutes or more.
type: integer
accessCodeLifespanUserAction:
default: 300
description: AccessCodeLifespanUserAction represents the max
time a user has to complete login related actions like update
password or configure totp. This is recommended to be relatively
long, such as 5 minutes or more.
type: integer
type: object
ssoOfflineSessionSettings:
description: SSOOfflineSessionSettings defines the SSO offline
session settings for the realm.
properties:
idleTimeout:
default: 2592000
description: |-
IdleTimeout represents the time an offline session is allowed to be idle before it expires.
You need to use offline token to refresh at least once within this period; otherwise offline session will expire.
type: integer
maxLifespan:
default: 5184000
description: MaxLifespan represents the max time before an
offline session is expired regardless of activity.
type: integer
maxLifespanEnabled:
default: false
description: MaxLifespanEnabled enables the offline session
maximum lifetime.
type: boolean
type: object
ssoSessionSettings:
description: SSOSessionSettings defines the SSO session settings
for the realm.
properties:
idleTimeout:
default: 1800
description: |-
IdleTimeout represents the time a session is allowed to be idle before it expires.
Tokens and browser sessions are invalidated when a session is expired.
type: integer
idleTimeoutRememberMe:
default: 0
description: |-
IdleTimeoutRememberMe represents the time a session is allowed to be idle before it expires.
Tokens and browser sessions are invalidated when a session is expired.
If not set it uses the standard ssoSessionIdle value.
type: integer
maxLifespan:
default: 36000
description: |-
MaxLifespan represents the max time before a session is expired.
Tokens and browser sessions are invalidated when a session is expired.
type: integer
maxLifespanRememberMe:
default: 0
description: |-
MaxLifespanRememberMe represents the max time before a session is expired when a user has set the remember me option.
Tokens and browser sessions are invalidated when a session is expired.
If not set it uses the standard ssoSessionMax value.
type: integer
type: object
type: object
smtp:
description: Smtp is the configuration for email in the realm.
nullable: true
@@ -174,10 +299,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -190,10 +318,13 @@ spec:
description: The key of the secret to select from.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -210,10 +341,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -226,10 +360,13 @@ spec:
description: The key of the secret to select from.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: clusterkeycloaks.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -66,10 +66,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -82,10 +85,13 @@ spec:
description: The key of the secret to select from.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakauthflows.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -109,15 +109,11 @@ spec:
description: ProviderID for root auth flow and provider for child
auth flows.
type: string
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -126,6 +122,8 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
topLevel:
description: TopLevel is true if this is root auth flow.
@@ -134,6 +132,7 @@ spec:
- alias
- builtIn
- providerId
- realmRef
- topLevel
type: object
status:

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakclients.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -45,8 +45,10 @@ spec:
description: KeycloakClientSpec defines the desired state of KeycloakClient.
properties:
adminFineGrainedPermissionsEnabled:
description: AdminFineGrainedPermissionsEnabled enable/disable fine-grained
admin permissions for a client.
description: |-
AdminFineGrainedPermissionsEnabled enable/disable fine-grained admin permissions for a client.
Feature flag admin-fine-grained-authz:v1 should be enabled in Keycloak server.
Important: FGAP:V1 Keycloak feature remains in preview and may be deprecated and removed in a future releases.
type: boolean
adminUrl:
description: |-
@@ -222,6 +224,8 @@ spec:
within an access token or ID token representing the identity asking permissions.
If not defined, user's groups are obtained from your realm configuration.
type: string
required:
- groups
type: object
logic:
default: POSITIVE
@@ -419,12 +423,36 @@ spec:
URI and tokens.
type: string
clientRoles:
description: ClientRoles is a list of client roles names assigned
to client.
description: |-
ClientRoles is a list of client roles names assigned to client.
Deprecated: Use ClientRolesV2 instead.
items:
type: string
nullable: true
type: array
clientRolesV2:
description: ClientRolesV2 is a list of client roles assigned to client.
items:
properties:
associatedClientRoles:
description: |-
AssociatedClientRoles is a list of client roles names associated with the current role.
These roles won't be created automatically, user should specify them separately in clientRolesV2.
items:
type: string
nullable: true
type: array
description:
description: Description is a client role description.
type: string
name:
description: Name is a client role name.
type: string
required:
- name
type: object
nullable: true
type: array
consentRequired:
description: ConsentRequired is a flag to enable consent.
type: boolean
@@ -524,6 +552,7 @@ spec:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -532,6 +561,8 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
realmRoles:
description: RealmRoles is a list of realm roles assigned to client.
@@ -582,7 +613,19 @@ spec:
attributes:
additionalProperties:
type: string
description: Attributes is a map of service account attributes.
description: |-
Attributes is a map of service account attributes.
Deprecated: Use AttributesV2 instead.
nullable: true
type: object
attributesV2:
additionalProperties:
items:
type: string
type: array
description: |-
AttributesV2 is a map of service account attributes.
Each attribute can have multiple values.
nullable: true
type: object
clientRoles:
@@ -595,7 +638,7 @@ spec:
type: string
roles:
description: Roles is a list of client roles names assigned
to service account.
to user.
items:
type: string
nullable: true
@@ -608,6 +651,12 @@ spec:
enabled:
description: Enabled is a flag to enable service account.
type: boolean
groups:
description: Groups is a list of groups assigned to service account
items:
type: string
nullable: true
type: array
realmRoles:
description: RealmRoles is a list of realm roles assigned to service
account.
@@ -623,13 +672,6 @@ spec:
surrogateAuthRequired:
description: SurrogateAuthRequired is a flag to enable surrogate auth.
type: boolean
targetRealm:
description: |-
Deprecated: use RealmRef instead.
TargetRealm is a realm name where client will be created.
It has higher priority than RealmRef for backward compatibility.
If both TargetRealm and RealmRef are specified, TargetRealm will be used for client creation.
type: string
webOrigins:
description: |-
WebOrigins is a list of allowed CORS origins.
@@ -647,12 +689,72 @@ spec:
type: string
required:
- clientId
- realmRef
type: object
status:
description: KeycloakClientStatus defines the observed state of KeycloakClient.
properties:
clientId:
type: string
conditions:
description: Conditions represent the latest available observations
of an object's state.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
nullable: true
type: array
failureCount:
format: int64
type: integer

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakclientscopes.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -52,7 +52,9 @@ spec:
nullable: true
type: object
default:
description: Default is a flag to set client scope as default.
description: |-
Default is a flag to set client scope as default.
Deprecated: Use Type: default instead.
type: boolean
description:
description: Description is a description of client scope.
@@ -87,15 +89,11 @@ spec:
type: object
nullable: true
type: array
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -104,10 +102,25 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
type:
default: none
description: |-
Type of the client scope.
If set to "default", the client scope is assigned to all clients by default.
If set to "optional", the client scope can be assigned to clients on demand.
If set to "none", the client scope is not assigned to any clients by default.
enum:
- default
- optional
- none
type: string
required:
- name
- protocol
- realmRef
type: object
status:
description: KeycloakClientScopeStatus defines the observed state of KeycloakClientScope.

View File

@@ -0,0 +1,149 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakorganizations.v1.edp.epam.com
spec:
group: v1.edp.epam.com
names:
kind: KeycloakOrganization
listKind: KeycloakOrganizationList
plural: keycloakorganizations
singular: keycloakorganization
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
- description: Keycloak organization ID
jsonPath: .status.organizationId
name: Organization ID
type: string
- description: Keycloak realm name
jsonPath: .spec.realmName
name: Realm
type: string
- description: Keycloak instance name
jsonPath: .spec.keycloakRef.name
name: Keycloak
type: string
name: v1alpha1
schema:
openAPIV3Schema:
description: KeycloakOrganization is the Schema for the organizations API.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: KeycloakOrganizationSpec defines the desired state of Organization.
properties:
alias:
description: |-
Alias is the unique alias for the organization.
The alias should be unique across Organizations.
type: string
attributes:
additionalProperties:
items:
type: string
type: array
description: Attributes is a map of custom attributes for the organization.
nullable: true
type: object
description:
description: Description is an optional description of the organization.
type: string
domains:
description: |-
Domains is a list of email domains associated with the organization.
Each domain should be unique across Organizations.
items:
type: string
minItems: 1
type: array
identityProviders:
description: |-
IdentityProviders is a list of identity providers associated with the organization.
One identity provider can't be assigned to multiple organizations.
items:
description: OrgIdentityProvider defines an identity provider for
an organization.
properties:
alias:
description: Alias is the unique identifier for the identity
provider within the organization.
type: string
required:
- alias
type: object
nullable: true
type: array
name:
description: |-
Name is the unique name of the organization.
The name should be unique across Organizations.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
- ClusterKeycloakRealm
type: string
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
redirectUrl:
description: RedirectURL is the optional redirect URL for the organization.
type: string
required:
- alias
- domains
- name
- realmRef
type: object
status:
description: KeycloakOrganizationStatus defines the observed state of
Organization.
properties:
error:
description: Error is the error message if the reconciliation failed.
type: string
organizationId:
description: OrganizationID is the unique identifier of the organization
in Keycloak.
type: string
value:
description: Value contains the current reconciliation status.
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealmcomponents.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -90,15 +90,11 @@ spec:
providerType:
description: ProviderType is a provider type of component.
type: string
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -107,11 +103,14 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
required:
- name
- providerId
- providerType
- realmRef
type: object
status:
description: KeycloakComponentStatus defines the observed state of KeycloakRealmComponent.

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealmgroups.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -67,7 +67,7 @@ spec:
type: string
roles:
description: Roles is a list of client roles names assigned
to service account.
to user.
items:
type: string
nullable: true
@@ -80,18 +80,28 @@ spec:
name:
description: Name of keycloak group.
type: string
parentGroup:
description: |-
ParentGroup is a reference to a parent KeycloakRealmGroup custom resource.
If specified, this group will be created as a child group of the referenced parent.
The parent KeycloakRealmGroup must exist in the same namespace.
nullable: true
properties:
name:
description: Name specifies the name of the KeycloakRealmGroup
custom resource.
type: string
required:
- name
type: object
path:
description: Path is a group path.
type: string
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -100,6 +110,8 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
realmRoles:
description: RealmRoles is a list of realm roles assigned to group.
@@ -108,13 +120,16 @@ spec:
nullable: true
type: array
subGroups:
description: SubGroups is a list of subgroups assigned to group.
description: |-
SubGroups is a list of subgroups assigned to group.
Deprecated: This filed doesn't allow to fully support child groups. Use ParentGroup approach instead.
items:
type: string
nullable: true
type: array
required:
- name
- realmRef
type: object
status:
description: KeycloakRealmGroupStatus defines the observed state of KeycloakRealmGroup.

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealmidentityproviders.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -50,6 +50,12 @@ spec:
description: AddReadTokenRoleOnCreate is a flag to add read token
role on create.
type: boolean
adminFineGrainedPermissionsEnabled:
description: |-
AdminFineGrainedPermissionsEnabled enable/disable fine-grained admin permissions for an identity provider.
Feature flag admin-fine-grained-authz:v1 should be enabled in Keycloak server.
Important: FGAP:V1 Keycloak feature remains in preview and may be deprecated and removed in a future releases.
type: boolean
alias:
description: Alias is a alias of identity provider.
type: string
@@ -102,18 +108,38 @@ spec:
type: object
nullable: true
type: array
permission:
description: Permission is a identity provider permissions configuration
nullable: true
properties:
scopePermissions:
description: ScopePermissions mapping of scope and the policies
attached
items:
properties:
name:
type: string
policies:
items:
type: string
type: array
required:
- name
type: object
type: array
type: object
postBrokerLoginFlowAlias:
description: PostBrokerLoginFlowAlias is a post broker login flow
alias.
type: string
providerId:
description: ProviderID is a provider ID of identity provider.
type: string
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -122,6 +148,8 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
storeToken:
description: StoreToken is a flag to store token.
@@ -134,6 +162,7 @@ spec:
- config
- enabled
- providerId
- realmRef
type: object
status:
description: KeycloakRealmIdentityProviderStatus defines the observed

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealmrolebatches.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -44,15 +44,11 @@ spec:
spec:
description: KeycloakRealmRoleBatchSpec defines the desired state of KeycloakRealmRoleBatch.
properties:
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -61,6 +57,8 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
roles:
description: Roles is a list of roles to be created.
@@ -104,6 +102,7 @@ spec:
type: object
type: array
required:
- realmRef
- roles
type: object
status:

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealmroles.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -98,15 +98,11 @@ spec:
name:
description: Name of keycloak role.
type: string
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -115,9 +111,12 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
required:
- name
- realmRef
type: object
status:
description: KeycloakRealmRoleStatus defines the observed state of KeycloakRealmRole.

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealms.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -19,7 +19,7 @@ spec:
jsonPath: .status.available
name: Available
type: boolean
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -83,16 +83,11 @@ spec:
description: ID is the ID of the realm.
nullable: true
type: string
keycloakOwner:
description: |-
Deprecated: use KeycloakRef instead.
KeycloakOwner specifies the name of the Keycloak instance that owns the realm.
nullable: true
type: string
keycloakRef:
description: KeycloakRef is reference to Keycloak custom resource.
properties:
kind:
default: Keycloak
description: Kind specifies the kind of the Keycloak resource.
enum:
- Keycloak
@@ -101,7 +96,60 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
login:
description: Login settings for the realm.
nullable: true
properties:
duplicateEmails:
default: false
description: DuplicateEmails allows multiple users to have the
same email address.
type: boolean
editUsername:
default: false
description: EditUsername allows to edit username.
type: boolean
emailAsUsername:
default: false
description: EmailAsUsername allows users to set email as username.
type: boolean
forgotPassword:
default: false
description: ForgotPassword shows a link on the login page for
users who have forgotten their credentials.
type: boolean
loginWithEmail:
default: true
description: LoginWithEmail allows users to log in with their
email address.
type: boolean
rememberMe:
default: false
description: RememberMe shows checkbox on the login page to allow
the user to remain logged in between browser restarts until
the session expires.
type: boolean
userRegistration:
default: false
description: UserRegistration enables/disables the registration
page. A link for registration will show on the login page too.
type: boolean
verifyEmail:
default: false
description: VerifyEmail requires user to verify their email address
after initial login or after address changes are submitted.
type: boolean
type: object
organizationsEnabled:
default: false
description: |-
OrganizationsEnabled enables Keycloak Organizations feature for this realm.
When enabled, this realm can support Organization resources for multi-tenant scenarios,
identity provider groupings, and domain-based user routing.
type: boolean
passwordPolicy:
description: PasswordPolicies is a list of password policies to apply
to the realm.
@@ -158,6 +206,83 @@ spec:
realmName:
description: RealmName specifies the name of the realm.
type: string
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
sessions:
description: Sessions defines the session settings for the realm.
properties:
ssoLoginSettings:
description: SSOLoginSettings defines the SSO login settings for
the realm.
properties:
accessCodeLifespanLogin:
default: 1800
description: AccessCodeLifespanLogin represents the max time
a user has to complete a login. This is recommended to be
relatively long, such as 30 minutes or more.
type: integer
accessCodeLifespanUserAction:
default: 300
description: AccessCodeLifespanUserAction represents the max
time a user has to complete login related actions like update
password or configure totp. This is recommended to be relatively
long, such as 5 minutes or more.
type: integer
type: object
ssoOfflineSessionSettings:
description: SSOOfflineSessionSettings defines the SSO offline
session settings for the realm.
properties:
idleTimeout:
default: 2592000
description: |-
IdleTimeout represents the time an offline session is allowed to be idle before it expires.
You need to use offline token to refresh at least once within this period; otherwise offline session will expire.
type: integer
maxLifespan:
default: 5184000
description: MaxLifespan represents the max time before an
offline session is expired regardless of activity.
type: integer
maxLifespanEnabled:
default: false
description: MaxLifespanEnabled enables the offline session
maximum lifetime.
type: boolean
type: object
ssoSessionSettings:
description: SSOSessionSettings defines the SSO session settings
for the realm.
properties:
idleTimeout:
default: 1800
description: |-
IdleTimeout represents the time a session is allowed to be idle before it expires.
Tokens and browser sessions are invalidated when a session is expired.
type: integer
idleTimeoutRememberMe:
default: 0
description: |-
IdleTimeoutRememberMe represents the time a session is allowed to be idle before it expires.
Tokens and browser sessions are invalidated when a session is expired.
If not set it uses the standard ssoSessionIdle value.
type: integer
maxLifespan:
default: 36000
description: |-
MaxLifespan represents the max time before a session is expired.
Tokens and browser sessions are invalidated when a session is expired.
type: integer
maxLifespanRememberMe:
default: 0
description: |-
MaxLifespanRememberMe represents the max time before a session is expired when a user has set the remember me option.
Tokens and browser sessions are invalidated when a session is expired.
If not set it uses the standard ssoSessionMax value.
type: integer
type: object
type: object
smtp:
description: Smtp is the configuration for email in the realm.
nullable: true
@@ -179,10 +304,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -195,10 +323,13 @@ spec:
description: The key of the secret to select from.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -215,10 +346,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -231,10 +365,13 @@ spec:
description: The key of the secret to select from.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -550,6 +687,7 @@ spec:
nullable: true
type: array
required:
- keycloakRef
- realmName
type: object
status:

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloakrealmusers.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -15,7 +15,7 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Reconcilation status
- description: Reconciliation status
jsonPath: .status.value
name: Status
type: string
@@ -47,9 +47,40 @@ spec:
attributes:
additionalProperties:
type: string
description: Attributes is a map of user attributes.
description: |-
Attributes is a map of user attributes.
Deprecated: Use AttributesV2 instead.
nullable: true
type: object
attributesV2:
additionalProperties:
items:
type: string
type: array
description: |-
AttributesV2 is a map of service account attributes.
Each attribute can have multiple values.
nullable: true
type: object
clientRoles:
description: ClientRoles is a list of client roles assigned to user.
items:
properties:
clientId:
description: ClientID is a client ID.
type: string
roles:
description: Roles is a list of client roles names assigned
to user.
items:
type: string
nullable: true
type: array
required:
- clientId
type: object
nullable: true
type: array
email:
description: Email is a user email.
type: string
@@ -68,6 +99,13 @@ spec:
type: string
nullable: true
type: array
identityProviders:
description: IdentityProviders is a list of identity providers aliases
linked to the user.
items:
type: string
nullable: true
type: array
keepResource:
default: true
description: |-
@@ -79,9 +117,9 @@ spec:
description: LastName is a user last name.
type: string
password:
description: Password is a user password. Allows to keep user password
within Custom Resource. For security concerns, it is recommended
to use PasswordSecret instead.
description: |-
Password is a user password. Allows to keep user password within Custom Resource. For security concerns, it is recommended to use PasswordSecret instead.
Deperecated: use PasswordSecret instead.
type: string
passwordSecret:
description: PasswordSecret defines Kubernetes secret Name and Key,
@@ -94,19 +132,19 @@ spec:
name:
description: Name is the name of the secret.
type: string
temporary:
default: false
description: Temporary indicates whether the password is temporary.
type: boolean
required:
- key
- name
type: object
realm:
description: |-
Deprecated: use RealmRef instead.
Realm is name of KeycloakRealm custom resource.
type: string
realmRef:
description: RealmRef is reference to Realm custom resource.
properties:
kind:
default: KeycloakRealm
description: Kind specifies the kind of the Keycloak resource.
enum:
- KeycloakRealm
@@ -115,11 +153,13 @@ spec:
name:
description: Name specifies the name of the Keycloak resource.
type: string
required:
- name
type: object
reconciliationStrategy:
description: |-
ReconciliationStrategy is a strategy for reconciliation. Possible values: full, create-only.
Default value: full. If set to create-only, user will be created only if it does not exist. If user exists, it will not be updated.
ReconciliationStrategy is a strategy for reconciliation. Possible values: full, addOnly.
Default value: full. If set to addOnly, user will be created only if it does not exist. If user exists, it will not be updated.
If set to full, user will be created if it does not exist, or updated if it exists.
type: string
requiredUserActions:
@@ -139,14 +179,79 @@ spec:
description: Username is a username in keycloak.
type: string
required:
- realmRef
- username
type: object
status:
description: KeycloakRealmUserStatus defines the observed state of KeycloakRealmUser.
properties:
conditions:
description: Conditions represent the latest available observations
of an object's state.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
nullable: true
type: array
failureCount:
format: int64
type: integer
lastSyncedPasswordSecretVersion:
description: |-
LastSyncedPasswordSecretVersion stores the ResourceVersion of the password secret
that was last successfully synced to Keycloak. Used to detect secret changes.
type: string
value:
type: string
type: object

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.18.0
name: keycloaks.v1.edp.epam.com
spec:
group: v1.edp.epam.com
@@ -64,10 +64,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key
@@ -80,10 +83,13 @@ spec:
description: The key of the secret to select from.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
required:
- key

View File

@@ -156,6 +156,32 @@ rules:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakorganizations
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakorganizations/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakorganizations/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:

View File

@@ -16,11 +16,20 @@ spec:
template:
metadata:
labels:
{{- include "keycloak-operator.selectorLabels" . | nindent 8 }}
name: {{ .Values.name }}
control-plane: controller-manager
{{- if hasKey .Values.podLabels "name" }}
{{ fail "The 'name' key is not allowed in podLabels" }}
{{- end }}
{{- range $key, $value := .Values.podLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
serviceAccountName: edp-{{ .Values.name }}
securityContext:
runAsNonRoot: true
{{- if .Values.securityContext }}
securityContext: {{ toYaml .Values.securityContext | nindent 8 }}
{{- end }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
@@ -28,12 +37,20 @@ spec:
containers:
- name: {{ .Values.name }}
# Replace this with the built image name
image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
image: {{ if .Values.image.registry }}{{ .Values.image.registry }}/{{ end }}{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
imagePullPolicy: "{{ .Values.imagePullPolicy }}"
command:
- /manager
securityContext:
allowPrivilegeEscalation: false
args:
- --metrics-bind-address=:8443
- --leader-elect
- --health-probe-bind-address=:8081
{{- if .Values.enableWebhooks }}
- --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs
{{- end }}
{{- if .Values.containerSecurityContext }}
securityContext: {{ toYaml .Values.containerSecurityContext | nindent 12 }}
{{- end }}
env:
- name: WATCH_NAMESPACE
{{- if .Values.clusterReconciliationEnabled }}
@@ -51,11 +68,26 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.name
{{- if .Values.extraVolumeMounts }}
- name: ENABLE_OWNER_REF
value: {{ .Values.enableOwnerRef | quote }}
- name: ENABLE_WEBHOOKS
value: {{ .Values.enableWebhooks | quote }}
{{- if or .Values.extraVolumeMounts .Values.enableWebhooks }}
volumeMounts:
{{- if .Values.enableWebhooks }}
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-certs
readOnly: true
{{- end }}
{{- if .Values.extraVolumeMounts }}
{{- toYaml .Values.extraVolumeMounts | nindent 12 }}
{{- end }}
{{- end }}
{{- if .Values.enableWebhooks }}
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
{{- end }}
livenessProbe:
httpGet:
@@ -71,8 +103,13 @@ spec:
periodSeconds: 10
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.extraVolumes }}
{{- if or .Values.extraVolumes .Values.enableWebhooks }}
volumes:
{{- if .Values.enableWebhooks }}
- name: webhook-certs
secret:
secretName: webhook-server-cert
{{- end }}
{{- if .Values.extraVolumes }}
{{- toYaml .Values.extraVolumes | nindent 8 }}
{{- end }}

View File

@@ -5,309 +5,82 @@ metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakauthflows
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakauthflows/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakauthflows/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakclients
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakclients/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakclients/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakclientscopes
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakclientscopes/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakclientscopes/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmcomponents
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmcomponents/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmcomponents/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmgroups
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmgroups/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmgroups/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmidentityproviders
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmidentityproviders/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmidentityproviders/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmrolebatches
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmrolebatches/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmrolebatches/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmroles
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmroles/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmroles/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealms
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealms/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealms/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmusers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmusers/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealmusers/status
verbs:
- get
- patch
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloaks
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloaks/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloaks/status
verbs:
- get
- patch
- update
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1
resources:
- configmap
verbs:
- get
- list
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakauthflows
- keycloakclients
- keycloakclientscopes
- keycloakorganizations
- keycloakrealmcomponents
- keycloakrealmgroups
- keycloakrealmidentityproviders
- keycloakrealmrolebatches
- keycloakrealmroles
- keycloakrealms
- keycloakrealmusers
- keycloaks
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakauthflows/finalizers
- keycloakclients/finalizers
- keycloakclientscopes/finalizers
- keycloakorganizations/finalizers
- keycloakrealmcomponents/finalizers
- keycloakrealmgroups/finalizers
- keycloakrealmidentityproviders/finalizers
- keycloakrealmrolebatches/finalizers
- keycloakrealmroles/finalizers
- keycloakrealms/finalizers
- keycloakrealmusers/finalizers
- keycloaks/finalizers
verbs:
- update
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakauthflows/status
- keycloakclients/status
- keycloakclientscopes/status
- keycloakorganizations/status
- keycloakrealmcomponents/status
- keycloakrealmgroups/status
- keycloakrealmidentityproviders/status
- keycloakrealmrolebatches/status
- keycloakrealmroles/status
- keycloakrealms/status
- keycloakrealmusers/status
- keycloaks/status
verbs:
- get
- patch
- update

View File

@@ -0,0 +1,25 @@
{{- if .Values.enableWebhooks }}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
name: {{ .Values.name }}-serving-cert
spec:
dnsNames:
- {{ .Values.name }}-webhook-service.{{ .Release.Namespace }}.svc
- {{ .Values.name }}-webhook-service.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}
issuerRef:
kind: Issuer
name: {{ .Values.name }}-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
name: {{ .Values.name }}-selfsigned-issuer
spec:
selfSigned: {}
{{- end }}

View File

@@ -0,0 +1,78 @@
{{- if .Values.enableWebhooks }}
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: {{ .Values.name }}-mutating-webhook-configuration-{{ .Release.Namespace }}
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.name }}-serving-cert
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: {{ .Values.name }}-webhook-service
namespace: {{ .Release.Namespace }}
path: /mutate-v1-edp-epam-com-v1-keycloakclient
failurePolicy: Fail
name: mkeycloakclient-v1.kb.io
{{- /* Namespace-scoped webhook to prevent cross-namespace conflicts. */ -}}
{{- if not .Values.clusterReconciliationEnabled }}
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- {{ .Release.Namespace }}
{{- end }}
rules:
- apiGroups:
- v1.edp.epam.com
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- keycloakclients
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.name }}-serving-cert
name: {{ .Values.name }}-validating-webhook-configuration-{{ .Release.Namespace }}
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: {{ .Values.name }}-webhook-service
namespace: {{ .Release.Namespace }}
path: /validate-v1-edp-epam-com-v1-keycloakrealm
failurePolicy: Fail
name: vkeycloakrealm-v1.kb.io
{{- /* Namespace-scoped webhook validation to prevent cross-namespace conflicts. */ -}}
{{- if not .Values.clusterReconciliationEnabled }}
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- {{ .Release.Namespace }}
{{- end }}
rules:
- apiGroups:
- v1.edp.epam.com
apiVersions:
- v1
operations:
- CREATE
resources:
- keycloakrealms
sideEffects: None
{{- end }}

View File

@@ -0,0 +1,33 @@
{{- if .Values.enableWebhooks }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
name: edp-{{ .Release.Namespace }}-webhook-clusterrole
rules:
- apiGroups:
- v1.edp.epam.com
resources:
- keycloakrealms
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
name: edp-{{ .Release.Namespace }}-webhook-clusterrolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edp-{{ .Release.Namespace }}-webhook-clusterrole
subjects:
- kind: ServiceAccount
name: edp-{{ .Values.name }}
namespace: {{ .Release.Namespace }}
{{- end }}

View File

@@ -0,0 +1,16 @@
{{- if .Values.enableWebhooks }}
apiVersion: v1
kind: Service
metadata:
labels:
{{- include "keycloak-operator.labels" . | nindent 4 }}
name: {{ .Values.name }}-webhook-service
spec:
ports:
- port: 443
protocol: TCP
targetPort: 9443
selector:
{{- include "keycloak-operator.selectorLabels" . | nindent 4 }}
control-plane: controller-manager
{{- end }}

View File

@@ -2,6 +2,10 @@
name: keycloak-operator
# -- Annotations to be added to the Deployment
annotations: {}
# -- Cluster domain for constructing service DNS names
clusterDomain: cluster.local
# -- Labels to be added to the pod
podLabels: {}
# -- Node labels for pod assignment
nodeSelector: {}
# -- Node tolerations for server scheduling to nodes with taints
@@ -9,6 +13,8 @@ tolerations: []
# -- Affinity for pod assignment
affinity: {}
image:
# -- KubeRocketCI keycloak-operator Docker image registry.
registry: ""
# -- KubeRocketCI keycloak-operator Docker image name. The released image can be found on [Dockerhub](https://hub.docker.com/r/epamedp/keycloak-operator)
repository: epamedp/keycloak-operator
# if not defined then .Chart.AppVersion is used
@@ -27,6 +33,16 @@ resources:
cpu: 50m
memory: 64Mi
# -- Deployment Security Context
# Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
securityContext:
runAsNonRoot: true
# -- Container Security Context
# Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
containerSecurityContext:
allowPrivilegeEscalation: false
# -- Additional volumes to be added to the pod
extraVolumes: []
# - name: custom-ca
@@ -44,3 +60,11 @@ extraVolumeMounts: []
# -- If clusterReconciliationEnabled is true, the operator reconciles all Keycloak instances in the cluster;
# otherwise, it only reconciles instances in the same namespace by default, and cluster-scoped resources are ignored.
clusterReconciliationEnabled: false
# -- If set to true, the operator will set the owner reference for all resources that have Keycloak or KeycloakRealm as reference.
# This is legacy behavior and not recommended for use. In the future, this will be set to false by default.
enableOwnerRef: true
# -- If set to true, enables webhook resources (ValidatingWebhookConfiguration, Service, and Certificate).
# Webhooks require cert-manager to be installed in the cluster.
enableWebhooks: true

View File

@@ -0,0 +1,29 @@
# syntax=docker/dockerfile:1.2
FROM alpine AS source
ARG COMMIT_REF=facbc36
RUN apk add --no-cache curl tar git
WORKDIR /src
RUN curl -sSL https://github.com/epam/edp-keycloak-operator/archive/${COMMIT_REF}.tar.gz \
| tar -xz --strip-components=1
COPY patches /patches
RUN git apply /patches/*.diff
FROM --platform=$BUILDPLATFORM docker.io/golang:1.24 AS builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /go/src/keycloak-operator
COPY --from=source /src/go.mod /src/go.sum ./
RUN go mod download
COPY --from=source /src .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /build/manager ./cmd
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /build/manager /manager
USER 65532:65532
ENTRYPOINT ["/manager"]

View File

@@ -0,0 +1,187 @@
diff --git a/internal/controller/keycloakrealmgroup/chain/chain.go b/internal/controller/keycloakrealmgroup/chain/chain.go
index 1ca497a0..dbc3844b 100644
--- a/internal/controller/keycloakrealmgroup/chain/chain.go
+++ b/internal/controller/keycloakrealmgroup/chain/chain.go
@@ -15,6 +15,10 @@ type GroupContext struct {
// GroupID is the Keycloak group ID, set by CreateOrUpdateGroup handler.
GroupID string
+ // ExistingGroupID is the Keycloak group ID from a previous reconciliation (status.ID).
+ // Used to detect renames: if set, the group is fetched by ID first.
+ ExistingGroupID string
+
// ParentGroupID is the parent group's Keycloak ID (empty if top-level).
ParentGroupID string
diff --git a/internal/controller/keycloakrealmgroup/chain/create_or_update_group.go b/internal/controller/keycloakrealmgroup/chain/create_or_update_group.go
index 0504d9dd..931fab27 100644
--- a/internal/controller/keycloakrealmgroup/chain/create_or_update_group.go
+++ b/internal/controller/keycloakrealmgroup/chain/create_or_update_group.go
@@ -33,17 +33,34 @@ func (h *CreateOrUpdateGroup) Serve(
err error
)
- if groupCtx.ParentGroupID != "" {
- existingGroup, _, err = kClient.Groups.FindChildGroupByName(ctx, realm, groupCtx.ParentGroupID, spec.Name)
- } else {
- existingGroup, _, err = kClient.Groups.FindGroupByName(ctx, realm, spec.Name)
+ // If we already have an ID from a previous reconciliation, fetch by ID first.
+ // This handles renames: spec.Name may have changed but the ID stays the same.
+ if groupCtx.ExistingGroupID != "" {
+ existingGroup, _, err = kClient.Groups.GetGroup(ctx, realm, groupCtx.ExistingGroupID)
+ if err != nil && !keycloakv2.IsNotFound(err) {
+ return fmt.Errorf("unable to get group by ID %q: %w", groupCtx.ExistingGroupID, err)
+ }
+
+ if keycloakv2.IsNotFound(err) {
+ log.Info("Group not found by ID, will search by name", "groupID", groupCtx.ExistingGroupID)
+ existingGroup = nil
+ }
}
- if err != nil && !keycloakv2.IsNotFound(err) {
- return fmt.Errorf("unable to search for group %q: %w", spec.Name, err)
+ // If we didn't find the group by ID, search by name.
+ if existingGroup == nil {
+ if groupCtx.ParentGroupID != "" {
+ existingGroup, _, err = kClient.Groups.FindChildGroupByName(ctx, realm, groupCtx.ParentGroupID, spec.Name)
+ } else {
+ existingGroup, _, err = kClient.Groups.FindGroupByName(ctx, realm, spec.Name)
+ }
+
+ if err != nil && !keycloakv2.IsNotFound(err) {
+ return fmt.Errorf("unable to search for group %q: %w", spec.Name, err)
+ }
}
- if keycloakv2.IsNotFound(err) {
+ if existingGroup == nil {
groupRep := keycloakv2.GroupRepresentation{
Name: &spec.Name,
Description: &spec.Description,
@@ -67,6 +84,7 @@ func (h *CreateOrUpdateGroup) Serve(
log.Info("Group created", "groupID", groupCtx.GroupID)
} else {
groupCtx.GroupID = *existingGroup.Id
+ existingGroup.Name = &spec.Name
existingGroup.Description = &spec.Description
existingGroup.Path = &spec.Path
existingGroup.Attributes = &spec.Attributes
diff --git a/internal/controller/keycloakrealmgroup/chain/create_or_update_group_test.go b/internal/controller/keycloakrealmgroup/chain/create_or_update_group_test.go
index 26841a71..000c7538 100644
--- a/internal/controller/keycloakrealmgroup/chain/create_or_update_group_test.go
+++ b/internal/controller/keycloakrealmgroup/chain/create_or_update_group_test.go
@@ -265,3 +265,97 @@ func TestCreateOrUpdateGroup_Serve_FindChildGroupError(t *testing.T) {
err := h.Serve(context.Background(), group, kClient, groupCtx)
assert.ErrorContains(t, err, "unable to search for group")
}
+
+func TestCreateOrUpdateGroup_Serve_RenameByID(t *testing.T) {
+ mockGroups := mocks.NewMockGroupsClient(t)
+
+ kClient := &keycloakv2.KeycloakClient{Groups: mockGroups}
+ groupCtx := &GroupContext{RealmName: "test-realm", ExistingGroupID: "existing-id"}
+
+ group := &keycloakApi.KeycloakRealmGroup{}
+ group.Spec.Name = "new-name"
+ group.Spec.Description = "Updated desc"
+ group.Spec.Path = "/new-name"
+ group.Spec.Attributes = map[string][]string{"key": {"val"}}
+
+ mockGroups.EXPECT().GetGroup(
+ context.Background(), "test-realm", "existing-id",
+ ).Return(&keycloakv2.GroupRepresentation{
+ Id: ptr.To("existing-id"),
+ Name: ptr.To("old-name"),
+ Path: ptr.To("/old-name"),
+ }, nil, nil)
+
+ mockGroups.EXPECT().UpdateGroup(
+ context.Background(), "test-realm", "existing-id",
+ keycloakv2.GroupRepresentation{
+ Id: ptr.To("existing-id"),
+ Name: ptr.To("new-name"),
+ Description: ptr.To("Updated desc"),
+ Path: ptr.To("/new-name"),
+ Attributes: &map[string][]string{"key": {"val"}},
+ },
+ ).Return(nil, nil)
+
+ h := NewCreateOrUpdateGroup()
+ err := h.Serve(context.Background(), group, kClient, groupCtx)
+ require.NoError(t, err)
+ assert.Equal(t, "existing-id", groupCtx.GroupID)
+}
+
+func TestCreateOrUpdateGroup_Serve_ExistingIDNotFound_FallsBackToName(t *testing.T) {
+ mockGroups := mocks.NewMockGroupsClient(t)
+
+ kClient := &keycloakv2.KeycloakClient{Groups: mockGroups}
+ groupCtx := &GroupContext{RealmName: "test-realm", ExistingGroupID: "deleted-id"}
+
+ group := &keycloakApi.KeycloakRealmGroup{}
+ group.Spec.Name = testGroupName
+ group.Spec.Path = "/test-group"
+ group.Spec.Attributes = map[string][]string{"key": {"val"}}
+
+ mockGroups.EXPECT().GetGroup(
+ context.Background(), "test-realm", "deleted-id",
+ ).Return(nil, nil, keycloakv2.ErrNotFound)
+
+ mockGroups.EXPECT().FindGroupByName(
+ context.Background(), "test-realm", testGroupName,
+ ).Return(nil, nil, keycloakv2.ErrNotFound)
+
+ mockGroups.EXPECT().CreateGroup(
+ context.Background(), "test-realm",
+ keycloakv2.GroupRepresentation{
+ Name: ptr.To(testGroupName),
+ Description: ptr.To(""),
+ Path: ptr.To("/test-group"),
+ Attributes: &map[string][]string{"key": {"val"}},
+ },
+ ).Return(&keycloakv2.Response{
+ HTTPResponse: &http.Response{
+ Header: http.Header{"Location": []string{"http://localhost/admin/realms/test-realm/groups/new-id"}},
+ },
+ }, nil)
+
+ h := NewCreateOrUpdateGroup()
+ err := h.Serve(context.Background(), group, kClient, groupCtx)
+ require.NoError(t, err)
+ assert.Equal(t, "new-id", groupCtx.GroupID)
+}
+
+func TestCreateOrUpdateGroup_Serve_GetGroupByIDError(t *testing.T) {
+ mockGroups := mocks.NewMockGroupsClient(t)
+
+ kClient := &keycloakv2.KeycloakClient{Groups: mockGroups}
+ groupCtx := &GroupContext{RealmName: "test-realm", ExistingGroupID: "existing-id"}
+
+ group := &keycloakApi.KeycloakRealmGroup{}
+ group.Spec.Name = testGroupName
+
+ mockGroups.EXPECT().GetGroup(
+ context.Background(), "test-realm", "existing-id",
+ ).Return(nil, nil, errors.New("connection error"))
+
+ h := NewCreateOrUpdateGroup()
+ err := h.Serve(context.Background(), group, kClient, groupCtx)
+ assert.ErrorContains(t, err, "unable to get group by ID")
+}
diff --git a/internal/controller/keycloakrealmgroup/keycloakrealmgroup_controller.go b/internal/controller/keycloakrealmgroup/keycloakrealmgroup_controller.go
index 0f7e6ce3..52015e24 100644
--- a/internal/controller/keycloakrealmgroup/keycloakrealmgroup_controller.go
+++ b/internal/controller/keycloakrealmgroup/keycloakrealmgroup_controller.go
@@ -166,8 +166,9 @@ func (r *ReconcileKeycloakRealmGroup) tryReconcile(ctx context.Context, keycloak
}
groupCtx := &chain.GroupContext{
- RealmName: realmName,
- ParentGroupID: parentGroupID,
+ RealmName: realmName,
+ ParentGroupID: parentGroupID,
+ ExistingGroupID: keycloakRealmGroup.Status.ID,
}
if err := chain.MakeChain().Serve(ctx, keycloakRealmGroup, kClientV2, groupCtx); err != nil {

Some files were not shown because too many files have changed in this diff Show More