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"