Skip to content
Open
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
59 changes: 59 additions & 0 deletions .github/workflows/helm-chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
################################################################################
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

name: "Helm Chart"

permissions:
contents: read

on:
pull_request:
branches: [main, release-*, ci-*]
paths:
- 'helm/**'
- '.github/workflows/helm-chart.yaml'
push:
branches: [main, release-*, ci-*]
paths:
- 'helm/**'
- '.github/workflows/helm-chart.yaml'

concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.number || github.run_id }}
cancel-in-progress: true

jobs:
test-helm-chart:
name: "Helm Lint and Unittest"
runs-on: ubuntu-latest
steps:
- name: "Checkout code"
uses: actions/checkout@v6

- name: "Setup Helm"
uses: azure/setup-helm@v4

- name: "Lint Helm chart"
run: helm lint ./helm

- name: "Run helm-unittest"
run: |
docker run --rm \
-u "$(id -u):$(id -g)" \
-v "${{ github.workspace }}":/apps \
helmunittest/helm-unittest helm/
4 changes: 2 additions & 2 deletions helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ This chart deploys an Apache Fluss cluster on Kubernetes, following Helm best pr
It requires a Zookeeper ensemble to be running in the same Kubernetes cluster. In future releases, we may add support for an embedded Zookeeper cluster.


## Development environment
## Development environment

| component | version |
| ------------------------------------------------------------------------------ | ------- |
Expand All @@ -33,7 +33,7 @@ It requires a Zookeeper ensemble to be running in the same Kubernetes cluster. I
| [Apache Fluss](https://fluss.apache.org/docs/) | v0.10.0-incubating |


## Image requirements
## Image requirements

A container image for Fluss is available on DockerHub as `fluss/fluss`. You can use it directly or build your own from this repo. To use your own image you need to build the project with [Maven](https://fluss.apache.org/community/dev/building/) and build it with Docker.

Expand Down
52 changes: 51 additions & 1 deletion helm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,54 @@ Selector labels
{{- define "fluss.selectorLabels" -}}
app.kubernetes.io/name: {{ include "fluss.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- end }}

{{/*
Generate JAAS configuration for SASL
*/}}
{{- define "fluss.sasl.jaasConfig" -}}
{{- if .Values.sasl.jaasConfig }}
{{- .Values.sasl.jaasConfig -}}
{{- else }}
FlussServer {
org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required
{{- range .Values.sasl.users }}
user_{{ .username }}="{{ .password }}"
{{- end }};
};
{{- end }}
{{- end }}

{{/*
Return true if SASL is configured in any of the listener protocols
*/}}
{{- define "fluss.isSaslEnabled" -}}
{{- $ctx := . -}}
{{- $res := "" -}}
{{- $keys := keys .Values.listeners | sortAlpha -}}
{{- range $keys }}
{{- $id := . -}}
{{- $l := index $ctx.Values.listeners $id -}}
{{- if regexFind "SASL" (upper $l.protocol) -}}
{{- $res = "true" -}}
{{- end -}}
{{- end -}}
{{- if $res -}}
{{- true -}}
{{- end -}}
{{- end -}}

{{/*
Generate ID:SECURITY list for listener protocols
*/}}
{{- define "fluss.listeners.protocolMap" -}}
{{- $ctx := . -}}
{{- $parts := list -}}
{{- $keys := keys .Values.listeners | sortAlpha -}}
{{- range $keys }}
{{- $id := . -}}
{{- $l := index $ctx.Values.listeners $id -}}
{{- $parts = append $parts (printf "%s:%s" (upper $id) (upper $l.protocol)) -}}
{{- end -}}
{{- join "," $parts -}}
{{- end }}
29 changes: 29 additions & 0 deletions helm/templates/secret-sasl.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

{{- if (include "fluss.isSaslEnabled" .) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "fluss.fullname" . }}-sasl-jaas-config
labels:
{{- include "fluss.labels" . | nindent 4 }}
type: Opaque
data:
jaas.conf: {{ include "fluss.sasl.jaasConfig" . | b64enc | quote }}
{{- end -}}
30 changes: 30 additions & 0 deletions helm/templates/sts-coordinator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ spec:
echo "" >> $FLUSS_HOME/conf/server.yaml && \
echo "bind.listeners: ${BIND_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \
echo "advertised.listeners: ${ADVERTISED_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \
echo "security.protocol.map: {{ include "fluss.listeners.protocolMap" . }}" >> $FLUSS_HOME/conf/server.yaml && \

{{- if (include "fluss.isSaslEnabled" .) }}
{{- $saslUsers := default (list) .Values.sasl.users -}}
{{- if eq (len $saslUsers) 0 -}}
{{- fail "sasl.users must contain at least one user when SASL is enabled in listeners" -}}
{{- end }}
{{- $jaasUsers := list -}}
{{- range $saslUsers }}
{{- $jaasUsers = append $jaasUsers (printf "user_%s=\\\"%s\\\"" .username .password) -}}
{{- end }}
{{- $clientUser := first $saslUsers -}}
echo "security.sasl.enabled.mechanisms: {{ .Values.sasl.mechanism }}" >> $FLUSS_HOME/conf/server.yaml && \
echo "security.sasl.plain.jaas.config: org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required {{ join " " $jaasUsers }};" >> $FLUSS_HOME/conf/server.yaml && \

echo "client.security.protocol: SASL" >> $FLUSS_HOME/conf/server.yaml && \
echo "client.security.sasl.mechanism: {{ .Values.sasl.mechanism }}" >> $FLUSS_HOME/conf/server.yaml && \
echo "client.security.sasl.username: {{ $clientUser.username }}" >> $FLUSS_HOME/conf/server.yaml && \
echo "client.security.sasl.password: {{ $clientUser.password }}" >> $FLUSS_HOME/conf/server.yaml && \
{{- end }}

bin/coordinator-server.sh start-foreground
livenessProbe:
Expand All @@ -100,6 +120,11 @@ spec:
mountPath: /opt/conf
- name: data
mountPath: /tmp/fluss/data
{{- if (include "fluss.isSaslEnabled" .) }}
- name: sasl-config
mountPath: /etc/fluss/conf
readOnly: true
{{- end }}
volumes:
- name: fluss-conf
configMap:
Expand All @@ -108,6 +133,11 @@ spec:
- name: data
emptyDir: {}
{{- end }}
{{- if (include "fluss.isSaslEnabled" .) }}
- name: sasl-config
secret:
secretName: {{ include "fluss.fullname" . }}-sasl-jaas-config
{{- end }}
{{- if .Values.coordinator.storage.enabled }}
volumeClaimTemplates:
- metadata:
Expand Down
30 changes: 30 additions & 0 deletions helm/templates/sts-tablet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ spec:
echo "tablet-server.id: ${FLUSS_SERVER_ID}" >> $FLUSS_HOME/conf/server.yaml && \
echo "bind.listeners: ${BIND_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \
echo "advertised.listeners: ${ADVERTISED_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \
echo "security.protocol.map: {{ include "fluss.listeners.protocolMap" . }}" >> $FLUSS_HOME/conf/server.yaml && \

{{- if (include "fluss.isSaslEnabled" .) }}
{{- $saslUsers := default (list) .Values.sasl.users -}}
{{- if eq (len $saslUsers) 0 -}}
{{- fail "sasl.users must contain at least one user when SASL is enabled in listeners" -}}
{{- end }}
{{- $jaasUsers := list -}}
{{- range $saslUsers }}
{{- $jaasUsers = append $jaasUsers (printf "user_%s=\\\"%s\\\"" .username .password) -}}
{{- end }}
{{- $clientUser := first $saslUsers -}}
echo "security.sasl.enabled.mechanisms: {{ .Values.sasl.mechanism }}" >> $FLUSS_HOME/conf/server.yaml && \
echo "security.sasl.plain.jaas.config: org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required {{ join " " $jaasUsers }};" >> $FLUSS_HOME/conf/server.yaml && \

echo "client.security.protocol: SASL" >> $FLUSS_HOME/conf/server.yaml && \
echo "client.security.sasl.mechanism: {{ .Values.sasl.mechanism }}" >> $FLUSS_HOME/conf/server.yaml && \
echo "client.security.sasl.username: {{ $clientUser.username }}" >> $FLUSS_HOME/conf/server.yaml && \
echo "client.security.sasl.password: {{ $clientUser.password }}" >> $FLUSS_HOME/conf/server.yaml && \
{{- end }}

bin/tablet-server.sh start-foreground
livenessProbe:
Expand All @@ -97,6 +117,11 @@ spec:
mountPath: /opt/conf
- name: data
mountPath: /tmp/fluss/data
{{- if (include "fluss.isSaslEnabled" .) }}
- name: sasl-config
mountPath: /etc/fluss/conf
readOnly: true
{{- end }}
volumes:
- name: fluss-conf
configMap:
Expand All @@ -105,6 +130,11 @@ spec:
- name: data
emptyDir: {}
{{- end }}
{{- if (include "fluss.isSaslEnabled" .) }}
- name: sasl-config
secret:
secretName: {{ include "fluss.fullname" . }}-sasl-jaas-config
{{- end }}
{{- if .Values.tablet.storage.enabled }}
volumeClaimTemplates:
- metadata:
Expand Down
120 changes: 120 additions & 0 deletions helm/tests/sasl_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

suite: sasl-disabled-secret
templates:
- templates/secret-sasl.yaml
tests:
- it: does not render sasl secret when sasl is disabled
set:
listeners.internal.protocol: PLAINTEXT
listeners.client.protocol: PLAINTEXT
asserts:
- hasDocuments:
count: 0
---

suite: sasl-disabled-statefulset
templates:
- templates/sts-coordinator.yaml
tests:
- it: does not render sasl settings in pod spec when sasl is disabled
set:
listeners.internal.protocol: PLAINTEXT
listeners.client.protocol: PLAINTEXT
asserts:
- notMatchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: 'security\.sasl\.enabled\.mechanisms:'
- notMatchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: 'security\.sasl\.plain\.jaas\.config:'
- notMatchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: 'client\.security\.sasl\.username:'
- notMatchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: 'client\.security\.sasl\.password:'
- notContains:
path: spec.template.spec.volumes
content:
name: sasl-config
---

suite: sasl-enabled-generated-secret
templates:
- templates/secret-sasl.yaml
tests:
- it: renders generated sasl secret when sasl is enabled
set:
listeners.internal.protocol: SASL_PLAINTEXT
listeners.client.protocol: SASL_PLAINTEXT
asserts:
- hasDocuments:
count: 1
- matchRegex:
path: metadata.name
pattern: ^RELEASE-NAME-fluss-sasl-jaas-config$
---

suite: sasl-enabled-statefulset-uses-generated-secret
templates:
- templates/sts-coordinator.yaml
tests:
- it: references generated secret and renders sasl config lines
set:
listeners.internal.protocol: SASL_PLAINTEXT
listeners.client.protocol: SASL_PLAINTEXT
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: sasl-config
secret:
secretName: RELEASE-NAME-fluss-sasl-jaas-config
- matchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: 'security\.sasl\.plain\.jaas\.config:'
---

suite: sasl-empty-users-fails
templates:
- templates/sts-coordinator.yaml
tests:
- it: fails with clear message when sasl users are empty
set:
listeners.internal.protocol: SASL_PLAINTEXT
listeners.client.protocol: SASL_PLAINTEXT
sasl.users: []
asserts:
- failedTemplate:
errorMessage: sasl.users must contain at least one user when SASL is enabled in listeners
---

suite: server-yaml-write-path
templates:
- templates/sts-tablet.yaml
tests:
- it: writes server yaml using expected destination path
asserts:
- matchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: cp /opt/conf/server\.yaml \$FLUSS_HOME/conf
- matchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: '>> \$FLUSS_HOME/conf/server\.yaml'
Loading