Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ jobs:
fail "a cluster-scoped object is rendered"
fi
rbac=$(helm template kates charts/kates/ -n team --set rbac.scope=namespace \
--api-versions monitoring.coreos.com/v1/PodMonitor \
--set networkPolicy.kafka.namespace=kafka-ns --show-only templates/rbac.yaml)
[ "$(grep -c '^ namespace: kafka-ns$' <<<"${rbac}")" = 2 ] \
|| fail "the Role and RoleBinding are not both in networkPolicy.kafka.namespace"
Expand All @@ -348,6 +349,10 @@ jobs:
if grep -E '^ +verbs:' <<<"${rbac}" | grep -vqE 'verbs: \["(get|list)"\]$'; then
fail "the Kafka-namespace Role grants more than get and list"
fi
rbac=$(helm template kates charts/kates/ -n team --set rbac.scope=namespace \
--set networkPolicy.kafka.namespace=kafka-ns --show-only templates/rbac.yaml)
grep -q 'podmonitors' <<<"${rbac}" && fail "the Role names PodMonitors where the API is not served"
[ "$(grep -c -- '- apiGroups:' <<<"${rbac}")" = 4 ] || fail "the Kafka-namespace Role has other than its 4 rules without PodMonitors"
out=$(helm template kates charts/kates/ --set rbac.scope=namespace \
--set rbac.kafkaNamespaceRole=false --show-only templates/rbac.yaml 2>&1) || true
grep -q 'could not find template' <<<"${out}" || fail "rbac.kafkaNamespaceRole=false still renders the Role"
Expand Down
5 changes: 4 additions & 1 deletion charts/kates-platform/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ type: application
# Patch: the umbrella packs the subcharts the v1.36.0 release pins: kates
# 0.15.7, kafka-cluster 1.0.29, connect-cluster 2.1.24, mirror-maker2 0.11.24.
# Patch: the chart icon; the subcharts it packs change only in theirs.
version: 0.8.23
# Patch: packs kates 0.15.9.
version: 0.8.24
appVersion: "1.0.0"
keywords:
- kafka
Expand Down Expand Up @@ -43,6 +44,8 @@ dependencies:
condition: connect-cluster.enabled
annotations:
artifacthub.io/changes: |
- kind: fixed
description: "Packs kates 0.15.9, whose Kafka-namespace Role carries its podmonitors rule only where the PodMonitor API exists."
- kind: added
description: "A Kates chart icon. Packs kates 0.15.8, kafka-cluster 1.0.30, apicurio-registry 0.5.1, mirror-maker2 0.11.25 and connect-cluster 2.1.25, which change only in their icons. Templates are unchanged."
- kind: changed
Expand Down
5 changes: 4 additions & 1 deletion charts/kates/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ type: application
# Patch: appVersion and the image tag move to 1.36.0, the tag the v1.36.0
# release publishes.
# Patch: the chart icon.
version: 0.15.8
# Patch: the Kafka-namespace Role's PodMonitor rule follows the cluster.
version: 0.15.9
# The non-blocking health endpoints this chart's probes now point at come from
# a BUILD-TIME property (quarkus.messaging.health.enabled=false), so they only
# exist in the image built from this branch. Keep appVersion and
Expand All @@ -29,6 +30,8 @@ annotations:
artifacthub.io/category: integration-delivery
artifacthub.io/license: Apache-2.0
artifacthub.io/changes: |
- kind: fixed
description: "With rbac.scope=namespace the Kafka-namespace Role carries its podmonitors rule only where the PodMonitor API exists, so a namespace admin can create it on a cluster without the Prometheus operator. helm template asks no cluster: pass --api-versions monitoring.coreos.com/v1/PodMonitor to render the rule for one that has it"
- kind: fixed
description: "The chart icon pointed at a file the repository does not have; it is now a Kates icon."
- kind: changed
Expand Down
5 changes: 3 additions & 2 deletions charts/kates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ that namespace (`kates/docs/deployment.md` runs one in `kates`) needs a
| `kafka.strimzi.io` `kafkas` | `get` |
| `kafka.strimzi.io` `kafkanodepools` | `list` |
| `pods`, `persistentvolumeclaims` | `list` |
| `monitoring.coreos.com` `podmonitors` | `list` |
| `monitoring.coreos.com` `podmonitors`, where the cluster serves that API | `list` |
| `discovery.k8s.io` `endpointslices` | `list` |

An installer with only `edit` in the release namespace can't create that Role, so it installs with `rbac.kafkaNamespaceRole=false` as well. Kubernetes lets an account create a Role only for permissions it holds itself, so an account with Strimzi's and the Prometheus Operator's reads in the Kafka namespace, a cluster admin where those operators don't add them to `admin`, renders it from the same release name, namespace and values, and applies it:
An installer with only `edit` in the release namespace can't create that Role, so it installs with `rbac.kafkaNamespaceRole=false` as well. Kubernetes lets an account create a Role only for permissions it holds itself, so an account with Strimzi's and the Prometheus Operator's reads in the Kafka namespace, a cluster admin where those operators don't add them to `admin`, renders it from the same release name, namespace and values, and applies it. `helm template` asks no cluster, so `--api-versions` keeps the PodMonitor rule; leave it out where the cluster has no Prometheus Operator:

```bash
helm template kates oci://ghcr.io/bmscomp/charts/kates -n team -f team-values.yaml \
--api-versions monitoring.coreos.com/v1/PodMonitor \
--set rbac.kafkaNamespaceRole=true --show-only templates/rbac.yaml | kubectl apply -f -
```

Expand Down
6 changes: 5 additions & 1 deletion charts/kates/templates/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ subjects:
rbac.scope=namespace: nothing cluster-scoped. Performance and INTEGRITY runs
make no Kubernetes call; a conformity check reads these in the Kafka
namespace (ConformityCheck). It lists PodMonitors across namespaces first,
then here when that is refused. Nodes stay unread: zones, skew and
then here when that is refused; the rule renders only where the
PodMonitor API exists, so an account can create this Role on a cluster
without it. helm template needs --api-versions for it. Nodes stay unread: zones, skew and
kafka.rackAwareness with source cr take each pod's zone from the
EndpointSlices of Strimzi's headless Service, unless rbac.nodeReads grants
them below. The release namespace ends
Expand All @@ -125,9 +127,11 @@ rules:
- apiGroups: [""]
resources: ["pods", "persistentvolumeclaims"]
verbs: ["list"]
{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1/PodMonitor" }}
- apiGroups: ["monitoring.coreos.com"]
resources: ["podmonitors"]
verbs: ["list"]
{{- end }}
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["list"]
Expand Down
4 changes: 2 additions & 2 deletions charts/kates/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ rbac:
# -- With scope namespace: render the read-only Role and RoleBinding a
# conformity check needs in the Kafka namespace (networkPolicy.kafka.namespace,
# which the check then reads). An installer with only edit sets it false, and
# an account that may grant those reads applies them from
# `helm template ... --show-only templates/rbac.yaml`.
# an account that may grant those reads applies them from `helm template ...
# --api-versions monitoring.coreos.com/v1/PodMonitor --show-only templates/rbac.yaml`.
kafkaNamespaceRole: true
# -- With scope namespace: render a ClusterRole and ClusterRoleBinding that
# grant get and list on nodes, so a conformity check reads any zoneLabel.
Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/cluster_gate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,9 @@ func setDeployFlags(t *testing.T, set func()) {
kyverno, strimzi, connect := deployWithKyverno, deployWithStrimzi, deployWithKafkaConnect
ui, mm2, dry := deployWithKafkaUI, deployWithMirrorMaker2, deployDryRun
namespaceLabels := append([]string(nil), deployNamespaceLabels...)
adminless, dbNS := deployAdminless, deployDbNS
adminless, dbNS, ha := deployAdminless, deployDbNS, deployHA
t.Cleanup(func() {
deployAdminless, deployDbNS = adminless, dbNS
deployAdminless, deployDbNS, deployHA = adminless, dbNS, ha
deployTopology, deployNamespace, deployWithSchemaRegistry = topology, namespace, registry
deployWithChaos, deployWithMonitoring, deployWithCertManager = chaos, monitoring, certManager
deployWithKyverno, deployWithStrimzi, deployWithKafkaConnect = kyverno, strimzi, connect
Expand Down
45 changes: 40 additions & 5 deletions cli/cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ func runDeploy(cmd *cobra.Command, args []string) error {
}
// Deploy makes each namespace itself, or uses the one already there.
defer dropCreateNamespace()()

executor := defaultExecutor
report, err := adminlessReportFirst(cmd, executor, target, namespaceLabels)
if err != nil {
return err
}
if err := configureDeploySelection(cmd, wizard); err != nil {
return err
}
Expand All @@ -202,14 +208,15 @@ func runDeploy(cmd *cobra.Command, args []string) error {
// go in before any pod, the pre-flight probes' included. A dry run loads
// nothing.
if !deployDryRun {
if err := loadBundleImages(context.Background(), target, quickDetectKind()); err != nil {
kubeContext, isKind := bundleKindTarget(target, report)
if err := loadBundleImages(context.Background(), kubeContext, isKind); err != nil {
return err
}
}

executor := defaultExecutor
report, err := introspectDeployCluster(executor, target, namespaceLabels)
if err != nil {
if report != nil {
sizeAdminless(context.Background(), report, cmd.Flags().Changed("ha"))
} else if report, err = introspectDeployCluster(executor, target, namespaceLabels); err != nil {
return err
}

Expand Down Expand Up @@ -476,13 +483,40 @@ func configureDeploySelection(cmd *cobra.Command, wizard bool) error {
return nil
}

// adminlessReportFirst reads the report before the wizard, admin-less only: it
// only reads, the wizard's HA question starts from the size this cluster takes,
// and it tells kind where quickDetectKind may not read.
func adminlessReportFirst(cmd *cobra.Command, executor detect.CommandExecutor, target string, namespaceLabels map[string]string) (*detect.DetectReport, error) {
if !deployAdminless {
return nil, nil
}
report, err := introspectDeployCluster(executor, target, namespaceLabels)
if err != nil {
return nil, err
}
defaultAdminlessSize(report, cmd.Flags().Changed("ha"))
return report, nil
}

// bundleKindTarget says which context the bundle's images load through and
// whether it is kind: the admin-less report's when it told kind, since that
// account may not read quickDetectKind's DaemonSet, and its context carries
// kind's own name, where a team's context may not.
func bundleKindTarget(target string, report *detect.DetectReport) (string, bool) {
if report != nil && isKindReport(report) {
return report.Context, true
}
return target, quickDetectKind()
}

// introspectDeployCluster runs the pre-flight and the introspection that
// sizes the values file. Admin-less it runs neither.
func introspectDeployCluster(executor detect.CommandExecutor, target string, namespaceLabels map[string]string) (*detect.DetectReport, error) {
if deployAdminless {
// Introspection reads nodes and kube-system, and its probes create
// namespaces and pods: nothing a namespace account may do. The charts'
// own defaults stand in for what it would have sized.
// own defaults stand in for what it would have sized, or the admin-less
// sizing shrinks them.
// shortcut: assumes the cluster.local DNS domain; read it from a pod in the namespace if a cluster needs another.
report := &detect.DetectReport{Context: target, Provider: "unknown"}
report.Network.ClusterDomain = "cluster.local"
Expand All @@ -491,6 +525,7 @@ func introspectDeployCluster(executor detect.CommandExecutor, target string, nam
collector := detect.NewCollector(executor)
collector.KubeContext = target
_ = collector.CollectNodes(context.Background(), report)
detectAdminlessPlatform(context.Background(), report)
return report, nil
}
PrintPhaseHeader(nextDeployPhase(), "Running Cluster Introspection (Pre-flight)")
Expand Down
Loading
Loading