Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add console subchart in redpanda helm chart #1558

Merged
merged 3 commits into from
Oct 17, 2024
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
6 changes: 3 additions & 3 deletions charts/redpanda/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
dependencies:
- name: console
repository: https://charts.redpanda.com
version: 0.7.29
version: 0.7.30
- name: connectors
repository: https://charts.redpanda.com
version: 0.1.13
digest: sha256:3023f8ca61cf80050d0f0e73f9e86b73ae796717c651be8765c9db90996e5462
generated: "2024-09-26T22:13:55.854012+02:00"
digest: sha256:f40126de897d3ec8d707b28408763bfad218f7c688575371f6fa3cb371eb884c
generated: "2024-10-16T12:27:15.358101+02:00"
2 changes: 2 additions & 0 deletions charts/redpanda/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ func render(dot *helmette.Dot) []kube.Object {
manifests = append(manifests, obj)
}

manifests = append(manifests, consoleChartIntegration(dot)...)

// NB: This slice may contain nil interfaces!
// Filtering happens elsewhere, don't call this function directly if you
// can avoid it.
Expand Down
22 changes: 18 additions & 4 deletions charts/redpanda/chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"time"

"github.com/pkg/errors"
"github.com/redpanda-data/helm-charts/charts/console"
"github.com/redpanda-data/helm-charts/charts/redpanda"
"github.com/redpanda-data/helm-charts/pkg/gotohelm/helmette"
"github.com/redpanda-data/helm-charts/pkg/helm"
Expand Down Expand Up @@ -685,7 +686,7 @@ func TestLabels(t *testing.T) {
values := &redpanda.PartialValues{
CommonLabels: labels,
// This guarantee does not currently extend to console.
Console: &redpanda.PartialConsole{Enabled: ptr.To(false)},
Console: &console.PartialValues{Enabled: ptr.To(false)},
// Nor connectors.
Connectors: &redpanda.PartialConnectors{Enabled: ptr.To(false)},
}
Expand Down Expand Up @@ -738,7 +739,9 @@ func TestGoHelmEquivalence(t *testing.T) {

// TODO: Add additional cases for better coverage. Generating random inputs
// generally results in invalid inputs.
var values redpanda.PartialValues
values := redpanda.PartialValues{
Enterprise: &redpanda.PartialEnterprise{License: ptr.To("LICENSE_PLACEHOLDER")},
}

// We're not interested in tests, console, or connectors so always disable
// those.
Expand All @@ -748,7 +751,18 @@ func TestGoHelmEquivalence(t *testing.T) {
Enabled: ptr.To(false),
}

values.Console = &redpanda.PartialConsole{Enabled: ptr.To(false)}
values.Console = &console.PartialValues{
Enabled: ptr.To(true),
Ingress: &console.PartialIngressConfig{
Enabled: ptr.To(true),
},
Secret: &console.PartialSecretConfig{
Login: &console.PartialLoginSecrets{
JWTSecret: ptr.To("JWT_PLACEHOLDER"),
},
},
Tests: &console.PartialEnableable{Enabled: ptr.To(false)},
}
values.Connectors = &redpanda.PartialConnectors{Enabled: ptr.To(false)}

goObjs, err := redpanda.Chart.Render(kube.Config{}, helmette.Release{
Expand Down Expand Up @@ -780,7 +794,7 @@ func TestGoHelmEquivalence(t *testing.T) {
return strings.Compare(aStr, bStr)
})

const stsIdx = 7
const stsIdx = 12

// resource.Quantity is a special object. To Ensure they compare correctly,
// we'll round trip it through JSON so the internal representations will
Expand Down
20 changes: 10 additions & 10 deletions charts/redpanda/configmap.tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ func rpkKafkaClientTLSConfiguration(dot *helmette.Dot) map[string]any {
}

if tls.RequireClientAuth {
result["cert_file"] = fmt.Sprintf("/etc/tls/certs/%s-client/tls.crt", Fullname(dot))
result["key_file"] = fmt.Sprintf("/etc/tls/certs/%s-client/tls.key", Fullname(dot))
result["cert_file"] = fmt.Sprintf("%s/%s-client/tls.crt", certificateMountPoint, Fullname(dot))
result["key_file"] = fmt.Sprintf("%s/%s-client/tls.key", certificateMountPoint, Fullname(dot))
}

return result
Expand All @@ -374,8 +374,8 @@ func rpkAdminAPIClientTLSConfiguration(dot *helmette.Dot) map[string]any {
}

if tls.RequireClientAuth {
result["cert_file"] = fmt.Sprintf("/etc/tls/certs/%s-client/tls.crt", Fullname(dot))
result["key_file"] = fmt.Sprintf("/etc/tls/certs/%s-client/tls.key", Fullname(dot))
result["cert_file"] = fmt.Sprintf("%s/%s-client/tls.crt", certificateMountPoint, Fullname(dot))
result["key_file"] = fmt.Sprintf("%s/%s-client/tls.key", certificateMountPoint, Fullname(dot))
}

return result
Expand Down Expand Up @@ -409,8 +409,8 @@ func kafkaClient(dot *helmette.Dot) map[string]any {
}

if kafkaTLS.RequireClientAuth {
brokerTLS["cert_file"] = fmt.Sprintf("/etc/tls/certs/%s-client/tls.crt", Fullname(dot))
brokerTLS["key_file"] = fmt.Sprintf("/etc/tls/certs/%s-client/tls.key", Fullname(dot))
brokerTLS["cert_file"] = fmt.Sprintf("%s/%s-client/tls.crt", certificateMountPoint, Fullname(dot))
brokerTLS["key_file"] = fmt.Sprintf("%s/%s-client/tls.key", certificateMountPoint, Fullname(dot))
}

}
Expand Down Expand Up @@ -496,8 +496,8 @@ func rpcListenersTLS(dot *helmette.Dot) map[string]any {

return map[string]any{
"enabled": true,
"cert_file": fmt.Sprintf("/etc/tls/certs/%s/tls.crt", certName),
"key_file": fmt.Sprintf("/etc/tls/certs/%s/tls.key", certName),
"cert_file": fmt.Sprintf("%s/%s/tls.crt", certificateMountPoint, certName),
"key_file": fmt.Sprintf("%s/%s/tls.key", certificateMountPoint, certName),
"require_client_auth": r.TLS.RequireClientAuth,
"truststore_file": r.TLS.TrustStoreFilePath(&values.TLS),
}
Expand All @@ -521,8 +521,8 @@ func createInternalListenerTLSCfg(tls *TLS, internal InternalTLS) map[string]any
return map[string]any{
"name": "internal",
"enabled": true,
"cert_file": fmt.Sprintf("/etc/tls/certs/%s/tls.crt", internal.Cert),
"key_file": fmt.Sprintf("/etc/tls/certs/%s/tls.key", internal.Cert),
"cert_file": fmt.Sprintf("%s/%s/tls.crt", certificateMountPoint, internal.Cert),
"key_file": fmt.Sprintf("%s/%s/tls.key", certificateMountPoint, internal.Cert),
"require_client_auth": internal.RequireClientAuth,
"truststore_file": internal.TrustStoreFilePath(tls),
}
Expand Down
188 changes: 186 additions & 2 deletions charts/redpanda/console.tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,187 @@ import (
"fmt"

"github.com/redpanda-data/console/backend/pkg/config"
"github.com/redpanda-data/helm-charts/charts/console"
"github.com/redpanda-data/helm-charts/pkg/gotohelm/helmette"
"github.com/redpanda-data/helm-charts/pkg/kube"
corev1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
)

func ConsoleConfig(dot *helmette.Dot) any {
// consoleChartIntegration plumbs redpanda connection information into the console subchart.
// It does this by calculating Kafka, Schema registry, Redpanda Admin API configuration
// from Redpanda chart values.
func consoleChartIntegration(dot *helmette.Dot) []kube.Object {
values := helmette.UnmarshalInto[Values](dot.Values)

if !ptr.Deref(values.Console.Enabled, true) {
return nil
}

consoleDot := dot.Subcharts["console"]
loadedValues := consoleDot.Values

consoleValue := helmette.UnmarshalInto[console.Values](consoleDot.Values)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a lot of comments to this function. There were a handful previously but we can definitely do better.

Noting that the console.* section of the redpanda chart disables the secret/configmap in order for the redpanda chart to render it with connection information will be a great start.

// Pass the same Redpanda License to Console
if license := GetLicenseLiteral(dot); license != "" && !ptr.Deref(values.Console.Secret.Create, false) {
consoleValue.Secret.Create = true
consoleValue.Secret.Enterprise = console.EnterpriseSecrets{License: ptr.To(license)}
}

// Create console configuration based on Redpanda helm chart values.
if !ptr.Deref(values.Console.ConfigMap.Create, false) {
consoleValue.ConfigMap.Create = true
consoleValue.Console.Config = ConsoleConfig(dot)
}

if !ptr.Deref(values.Console.Deployment.Create, false) {
consoleValue.Deployment.Create = true

// Adopt Console entry point to use SASL user in Kafka,
// Schema Registry and Redpanda Admin API connection
if values.Auth.IsSASLEnabled() {
command := []string{
"sh",
"-c",
"set -e; IFS=':' read -r KAFKA_SASL_USERNAME KAFKA_SASL_PASSWORD KAFKA_SASL_MECHANISM < <(grep \"\" $(find /mnt/users/* -print));" +
fmt.Sprintf(" KAFKA_SASL_MECHANISM=${KAFKA_SASL_MECHANISM:-%s};", SASLMechanism(dot)) +
" export KAFKA_SASL_USERNAME KAFKA_SASL_PASSWORD KAFKA_SASL_MECHANISM;" +
" export KAFKA_SCHEMAREGISTRY_USERNAME=$KAFKA_SASL_USERNAME;" +
" export KAFKA_SCHEMAREGISTRY_PASSWORD=$KAFKA_SASL_PASSWORD;" +
" export REDPANDA_ADMINAPI_USERNAME=$KAFKA_SASL_USERNAME;" +
" export REDPANDA_ADMINAPI_PASSWORD=$KAFKA_SASL_PASSWORD;" +
" /app/console $@",
" --",
}
consoleValue.Deployment.Command = command
}

// Create License reference for Console
if secret := GetLicenseSecretReference(dot); secret != nil {
consoleValue.Enterprise = console.Enterprise{
LicenseSecretRef: console.SecretKeyRef{
Name: secret.Name,
Key: secret.Key,
},
}
}

consoleValue.ExtraVolumes = consoleTLSVolumes(dot)
consoleValue.ExtraVolumeMounts = consoleTLSVolumesMounts(dot)

consoleDot.Values = helmette.UnmarshalInto[helmette.Values](consoleValue)
cfg := console.ConfigMap(consoleDot)
if consoleValue.PodAnnotations == nil {
consoleValue.PodAnnotations = map[string]string{}
}
consoleValue.PodAnnotations["checksum-redpanda-chart/config"] = helmette.Sha256Sum(helmette.ToYaml(cfg))

}

consoleDot.Values = helmette.UnmarshalInto[helmette.Values](consoleValue)

manifests := []kube.Object{
console.Secret(consoleDot),
console.ConfigMap(consoleDot),
console.Deployment(consoleDot),
}

consoleDot.Values = loadedValues

// NB: This slice may contain nil interfaces!
// Filtering happens elsewhere, don't call this function directly if you
// can avoid it.
return manifests
}

func consoleTLSVolumesMounts(dot *helmette.Dot) []corev1.VolumeMount {
values := helmette.Unwrap[Values](dot.Values)

mounts := []corev1.VolumeMount{}

if sasl := values.Auth.SASL; sasl.Enabled && sasl.SecretRef != "" {
mounts = append(mounts, corev1.VolumeMount{
Name: fmt.Sprintf("%s-users", Fullname(dot)),
MountPath: "/mnt/users",
ReadOnly: true,
})
}

if len(values.Listeners.TrustStores(&values.TLS)) > 0 {
mounts = append(
mounts,
corev1.VolumeMount{Name: "truststores", MountPath: TrustStoreMountPath, ReadOnly: true},
)
}

visitedCert := map[string]bool{}
for _, tlsCfg := range []InternalTLS{
values.Listeners.Kafka.TLS,
values.Listeners.SchemaRegistry.TLS,
values.Listeners.Admin.TLS,
} {
_, visited := visitedCert[tlsCfg.Cert]
if !tlsCfg.IsEnabled(&values.TLS) || visited {
continue
}
visitedCert[tlsCfg.Cert] = true

mounts = append(mounts, corev1.VolumeMount{
Name: fmt.Sprintf("redpanda-%s-cert", tlsCfg.Cert),
MountPath: fmt.Sprintf("%s/%s", certificateMountPoint, tlsCfg.Cert),
})
}

return mounts
}

func consoleTLSVolumes(dot *helmette.Dot) []corev1.Volume {
values := helmette.Unwrap[Values](dot.Values)

volumes := []corev1.Volume{}

if sasl := values.Auth.SASL; sasl.Enabled && sasl.SecretRef != "" {
volumes = append(volumes, corev1.Volume{
Name: fmt.Sprintf("%s-users", Fullname(dot)),
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: values.Auth.SASL.SecretRef,
},
},
})
}

if vol := values.Listeners.TrustStoreVolume(&values.TLS); vol != nil {
volumes = append(volumes, *vol)
}

visitedCert := map[string]bool{}
for _, tlsCfg := range []InternalTLS{
values.Listeners.Kafka.TLS,
values.Listeners.SchemaRegistry.TLS,
values.Listeners.Admin.TLS,
} {
_, visited := visitedCert[tlsCfg.Cert]
if !tlsCfg.IsEnabled(&values.TLS) || visited {
continue
}
visitedCert[tlsCfg.Cert] = true

volumes = append(volumes, corev1.Volume{
Name: fmt.Sprintf("redpanda-%s-cert", tlsCfg.Cert),
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
DefaultMode: ptr.To[int32](0o420),
SecretName: CertSecretName(dot, tlsCfg.Cert, values.TLS.Certs.MustGet(tlsCfg.Cert)),
},
},
})
}

return volumes
}

func ConsoleConfig(dot *helmette.Dot) map[string]any {
values := helmette.Unwrap[Values](dot.Values)

var schemaURLs []string
Expand All @@ -42,13 +219,14 @@ func ConsoleConfig(dot *helmette.Dot) any {
if values.Listeners.Admin.TLS.IsEnabled(&values.TLS) {
schema = "https"
}

c := map[string]any{
"kafka": map[string]any{
"brokers": BrokerList(dot, values.Statefulset.Replicas, values.Listeners.Kafka.Port),
"sasl": map[string]any{
"enabled": values.Auth.IsSASLEnabled(),
},
"tls": values.Listeners.Kafka.ConsolemTLS(&values.TLS),
"tls": values.Listeners.Kafka.ConsoleTLS(&values.TLS),
"schemaRegistry": map[string]any{
"enabled": values.Listeners.SchemaRegistry.Enabled,
"urls": schemaURLs,
Expand Down Expand Up @@ -105,6 +283,12 @@ func ConsoleConfig(dot *helmette.Dot) any {
}
}

if values.Console.Console == nil {
values.Console.Console = &console.PartialConsole{
Config: map[string]any{},
}
}

return helmette.Merge(values.Console.Console.Config, c)
}

Expand Down
4 changes: 2 additions & 2 deletions charts/redpanda/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,15 @@ func CommonMounts(dot *helmette.Dot) []corev1.VolumeMount {

mounts = append(mounts, corev1.VolumeMount{
Name: fmt.Sprintf("redpanda-%s-cert", name),
MountPath: fmt.Sprintf("/etc/tls/certs/%s", name),
MountPath: fmt.Sprintf("%s/%s", certificateMountPoint, name),
})
}

adminTLS := values.Listeners.Admin.TLS
if adminTLS.RequireClientAuth {
mounts = append(mounts, corev1.VolumeMount{
Name: "mtls-client",
MountPath: fmt.Sprintf("/etc/tls/certs/%s-client", Fullname(dot)),
MountPath: fmt.Sprintf("%s/%s-client", certificateMountPoint, Fullname(dot)),
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion charts/redpanda/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ func adminTLSCurlFlags(dot *helmette.Dot) string {
}

if values.Listeners.Admin.TLS.RequireClientAuth {
path := fmt.Sprintf("/etc/tls/certs/%s-client", Fullname(dot))
path := fmt.Sprintf("%s/%s-client", certificateMountPoint, Fullname(dot))
return fmt.Sprintf("--cacert %s/ca.crt --cert %s/tls.crt --key %s/tls.key", path, path, path)
}

Expand Down
1 change: 1 addition & 0 deletions charts/redpanda/templates/_chart.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- $manifests = (concat (default (list ) $manifests) (default (list ) (get (fromJson (include "redpanda.consoleChartIntegration" (dict "a" (list $dot) ))) "r"))) -}}
{{- $_is_returning = true -}}
{{- (dict "r" $manifests) | toJson -}}
{{- break -}}
Expand Down
Loading