From 4c7a019adfb8eda50eede3e51b18795381c352cd Mon Sep 17 00:00:00 2001 From: Wyatt Walter Date: Wed, 4 Jun 2025 10:24:46 -0500 Subject: [PATCH] fix: support nss wrapper in Helm chart (#40673) ## Description Requires: https://github.com/appsmithorg/appsmith/pull/40642 Adds an environment variable activating nss_wrapper when the `securityPolicy.runAsUser` value is set so the UID can be set dynamically. This avoids the `I have no name!` in the prompt when doing a `kubectl exec` with that value set. I am also introducing [helm-unittest](https://github.com/helm-unittest/helm-unittest) for ensuring that changes to our defaults are made explicit. Fixes https://github.com/appsmithorg/appsmith/issues/38787 ## Automation /ok-to-test tags="" ### :mag: Cypress test results > [!WARNING] > Tests have not run on the HEAD d30d87ffc66c107f980a3b27464e97db0910dcbe yet >
Mon, 19 May 2025 18:59:10 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit - **New Features** - Added automated unit testing for Helm charts, including snapshot and security context tests. - Introduced documentation for running and understanding Helm chart unit tests. - Added a GitHub Actions workflow to run Helm chart unit tests on pull requests and manually. - **Bug Fixes** - Ensured the LD_PRELOAD environment variable is set when a specific security context is configured in deployments. - **Documentation** - Updated Helm chart README to reference new testing documentation and improve clarity. --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Goutham Pratapa --- .github/workflows/helm-unittest.yml | 26 +++ deploy/helm/Chart.yaml | 2 +- deploy/helm/README.md | 5 +- deploy/helm/templates/deployment.yaml | 5 + deploy/helm/tests/README.md | 37 +++ .../defaults_snapshot_test.yaml.snap | 210 ++++++++++++++++++ deploy/helm/tests/defaults_snapshot_test.yaml | 9 + deploy/helm/tests/runAsUser_test.yaml | 27 +++ 8 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/helm-unittest.yml create mode 100644 deploy/helm/tests/README.md create mode 100644 deploy/helm/tests/__snapshot__/defaults_snapshot_test.yaml.snap create mode 100644 deploy/helm/tests/defaults_snapshot_test.yaml create mode 100644 deploy/helm/tests/runAsUser_test.yaml diff --git a/.github/workflows/helm-unittest.yml b/.github/workflows/helm-unittest.yml new file mode 100644 index 0000000000..c818cbd434 --- /dev/null +++ b/.github/workflows/helm-unittest.yml @@ -0,0 +1,26 @@ +name: Helm Unit Tests + +on: + pull_request: + branches: + - release + paths: + - "deploy/helm/**" + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: deploy/helm + shell: bash + + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Unittest + run: | + docker run --rm -v $(pwd):/apps helmunittest/helm-unittest . diff --git a/deploy/helm/Chart.yaml b/deploy/helm/Chart.yaml index 011012bcd3..804fb651a0 100644 --- a/deploy/helm/Chart.yaml +++ b/deploy/helm/Chart.yaml @@ -11,7 +11,7 @@ sources: - https://github.com/appsmithorg/appsmith home: https://www.appsmith.com/ icon: https://assets.appsmith.com/appsmith-icon.png -version: 3.6.2 +version: 3.6.3 dependencies: - condition: redis.enabled name: redis diff --git a/deploy/helm/README.md b/deploy/helm/README.md index f4b3d087be..8929dc22ba 100644 --- a/deploy/helm/README.md +++ b/deploy/helm/README.md @@ -1,4 +1,3 @@ - # Appsmith Appsmith is a JS-based internal tool development platform. Internal tools take a lot of time to build even though they involve the same UI components, data integrations, and user access management. Developers love Appsmith because it saves them hundreds of hours. @@ -228,3 +227,7 @@ helm upgrade --values values.yaml stable-appsmith/appsmith appsmith If at any time you encounter an error during the installation process, reach out to support@appsmith.com or join our Discord Server If you know the error and would like to reinstall Appsmith, simply delete the installation folder and the templates folder and execute the script again + +## Testing + +Review tests/README.md for details on how the chart is tested. diff --git a/deploy/helm/templates/deployment.yaml b/deploy/helm/templates/deployment.yaml index 876a199b6c..b04241be18 100644 --- a/deploy/helm/templates/deployment.yaml +++ b/deploy/helm/templates/deployment.yaml @@ -144,6 +144,11 @@ spec: value: kubernetes.KUBE_PING - name: APPSMITH_HEADLESS_SVC value: {{ include "appsmith.fullname" . }}-headless + {{- if .Values.securityContext.runAsUser | default nil }} + # ensure the interactive shell when connected via kubectl exec is set + - name: LD_PRELOAD + value: /usr/local/lib/libnss_wrapper.so + {{- end }} envFrom: - configMapRef: name: {{ include "appsmith.fullname" . }} diff --git a/deploy/helm/tests/README.md b/deploy/helm/tests/README.md new file mode 100644 index 0000000000..6e906e8b81 --- /dev/null +++ b/deploy/helm/tests/README.md @@ -0,0 +1,37 @@ +# Helm Chart Unit Tests + +This directory contains unit tests for our Helm charts using [helm-unittest](https://github.com/helm-unittest/helm-unittest), a BDD-style testing framework for Helm charts. + +## Running Tests Locally + +You can run the tests locally using Docker: + +```bash +docker run -ti --rm -v $(pwd):/apps helmunittest/helm-unittest . +``` + +## Snapshot Testing + +Our tests use snapshot testing to validate the rendered Kubernetes manifests. This ensures that any changes to the defaults are intentional and reviewed. + +### Updating Snapshots + +When making changes that affect the rendered output (like updating labels or other metadata), you'll need to update the snapshots. This is particularly important during releases when labels are updated. + +To update snapshots, run the tests with the `-u` flag: + +```bash +docker run -ti --rm -v $(pwd):/apps helmunittest/helm-unittest -u . +``` + +**Important**: Always review the changes in the snapshots before committing them to ensure they match your expectations. + +## Documentation + +For more information about helm-unittest, including: +- Writing test cases +- Available assertions +- Test suite configuration +- Best practices + +Please refer to the [official helm-unittest documentation](https://github.com/helm-unittest/helm-unittest). diff --git a/deploy/helm/tests/__snapshot__/defaults_snapshot_test.yaml.snap b/deploy/helm/tests/__snapshot__/defaults_snapshot_test.yaml.snap new file mode 100644 index 0000000000..8a83829651 --- /dev/null +++ b/deploy/helm/tests/__snapshot__/defaults_snapshot_test.yaml.snap @@ -0,0 +1,210 @@ +"": + 1: | + raw: | + 1. Get the application URL by running these commands: + export POD_NAME=$(kubectl get pods --namespace NAMESPACE -l "app.kubernetes.io/name=appsmith,app.kubernetes.io/instance=RELEASE-NAME" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace NAMESPACE $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace NAMESPACE port-forward $POD_NAME 8080:$CONTAINER_PORT + + To expose your Appsmith service to be accessible from the Internet, please refer to our docs here https://docs.appsmith.com/getting-started/setup/installation-guides/kubernetes/publish-appsmith-online. + 2: | + apiVersion: v1 + data: + APPSMITH_DB_URL: | + mongodb+srv://root:password@appsmith-mongodb.NAMESPACE.svc.cluster.local/appsmith?retryWrites=true&authSource=admin&ssl=false + APPSMITH_DISABLE_IFRAME_WIDGET_SANDBOX: "false" + APPSMITH_KEYCLOAK_DB_DRIVER: postgresql + APPSMITH_KEYCLOAK_DB_PASSWORD: password + APPSMITH_KEYCLOAK_DB_URL: RELEASE-NAME-postgresql.NAMESPACE.svc.cluster.local:5432/keycloak + APPSMITH_KEYCLOAK_DB_USERNAME: root + APPSMITH_REDIS_URL: redis://RELEASE-NAME-redis-master.NAMESPACE.svc.cluster.local:6379 + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: appsmith + appsmith.sh/chart: appsmith-3.6.2 + name: RELEASE-NAME-appsmith + namespace: NAMESPACE + 3: | + apiVersion: apps/v1 + kind: StatefulSet + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: appsmith + appsmith.sh/chart: appsmith-3.6.2 + name: RELEASE-NAME-appsmith + namespace: NAMESPACE + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: appsmith + serviceName: RELEASE-NAME-appsmith + template: + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: appsmith + spec: + containers: + - env: + - name: APPSMITH_ENABLE_EMBEDDED_DB + value: "0" + - name: JGROUPS_DISCOVERY_PROTOCOL + value: kubernetes.KUBE_PING + - name: APPSMITH_HEADLESS_SVC + value: RELEASE-NAME-appsmith-headless + envFrom: + - configMapRef: + name: RELEASE-NAME-appsmith + image: index.docker.io/appsmith/appsmith-ee:latest + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /api/v1/health + port: 80 + periodSeconds: 60 + name: appsmith + ports: + - containerPort: 80 + name: http + protocol: TCP + - containerPort: 443 + name: https + protocol: TCP + - containerPort: 2019 + name: metrics + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /api/v1/health + port: 80 + periodSeconds: 60 + resources: + limits: {} + requests: + cpu: 500m + memory: 3000Mi + securityContext: {} + startupProbe: + failureThreshold: 3 + httpGet: + path: /api/v1/health + port: 80 + periodSeconds: 60 + volumeMounts: + - mountPath: /appsmith-stacks + name: data + initContainers: + - command: + - sh + - -c + - until redis-cli -h RELEASE-NAME-redis-master.NAMESPACE.svc.cluster.local ping ; do echo waiting for redis; sleep 2; done + image: docker.io/redis:7.0.15 + name: redis-init-container + - command: + - sh + - -c + - until mongosh --host appsmith-mongodb.NAMESPACE.svc.cluster.local --eval 'db.runCommand({ping:1})' ; do echo waiting for mongo; sleep 2; done + image: docker.io/bitnami/mongodb:6.0.13 + name: mongo-init-container + - command: + - sh + - -c + - until pg_isready -U $postgresuser -d $postgresdb -h RELEASE-NAME-postgresql.NAMESPACE.svc.cluster.local; do echo waiting for postgresql; sleep 2; done + image: docker.io/bitnami/postgresql:14.5.0-debian-11-r21 + name: psql-init-container + securityContext: {} + serviceAccountName: RELEASE-NAME-appsmith + volumes: null + updateStrategy: null + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + 4: | + apiVersion: v1 + kind: Service + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: appsmith + appsmith.sh/chart: appsmith-3.6.2 + name: RELEASE-NAME-appsmith-headless + namespace: NAMESPACE + spec: + clusterIP: None + clusterIPs: + - None + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: http + port: 8080 + targetPort: 8080 + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: appsmith + type: ClusterIP + 5: | + apiVersion: policy/v1beta1 + kind: PodDisruptionBudget + metadata: + name: RELEASE-NAME-appsmith-pdb + namespace: NAMESPACE + spec: + minAvailable: 1 + selector: + matchLabels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: appsmith + 6: | + apiVersion: v1 + kind: Service + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: appsmith + appsmith.sh/chart: appsmith-3.6.2 + name: RELEASE-NAME-appsmith + namespace: NAMESPACE + spec: + ports: + - name: appsmith + nodePort: null + port: 80 + targetPort: http + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: appsmith + type: ClusterIP + 7: | + apiVersion: v1 + kind: ServiceAccount + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: appsmith + appsmith.sh/chart: appsmith-3.6.2 + name: RELEASE-NAME-appsmith + namespace: NAMESPACE + secrets: + - name: RELEASE-NAME-appsmith diff --git a/deploy/helm/tests/defaults_snapshot_test.yaml b/deploy/helm/tests/defaults_snapshot_test.yaml new file mode 100644 index 0000000000..f5c706e32f --- /dev/null +++ b/deploy/helm/tests/defaults_snapshot_test.yaml @@ -0,0 +1,9 @@ +# snapshot tests to capture expected changes to defaults + +# exclude dependent charts from the snapshot +excludeTemplates: + - charts/* +tests: + - name: manifest should match snapshot from default values + asserts: + - matchSnapshot: {} diff --git a/deploy/helm/tests/runAsUser_test.yaml b/deploy/helm/tests/runAsUser_test.yaml new file mode 100644 index 0000000000..dac0c3420e --- /dev/null +++ b/deploy/helm/tests/runAsUser_test.yaml @@ -0,0 +1,27 @@ +templates: + - deployment.yaml +tests: + - name: runAsUser should be 9999 + set: + podSecurityContext: + sysctls: + - name: net.ipv4.ip_unprivileged_port_start + value: "80" + securityContext: + runAsNonRoot: true + runAsUser: 9999 + asserts: + - equal: + path: spec.template.spec.containers[?(@.name == "appsmith")].securityContext + value: + runAsUser: 9999 + runAsNonRoot: true + - equal: + path: spec.template.spec.securityContext + value: + sysctls: + - name: net.ipv4.ip_unprivileged_port_start + value: "80" + - equal: + path: spec.template.spec.containers[?(@.name == "appsmith")].env[?(@.name == "LD_PRELOAD")].value + value: "/usr/local/lib/libnss_wrapper.so"