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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## [1.5.1] - 2026-03-27

### Added
- **Wait for Directory Init Container**: New template `harnesscommon.initContainer.waitForDirectory` that creates an init container to wait for a directory to exist on the Kubernetes node before starting the main container
- Configurable timeout (default: 300s) and check interval (default: 2s)
- Uses busybox:1.36 by default with full image override support via `common.images.image` helper
- Read-only mount for safety
- Progress logging with elapsed time tracking
- See [Init Containers documentation](docs/INIT_CONTAINERS.md) for usage examples

### Changed
- Added comprehensive CI test coverage for wait-for-directory template
- Template smoke tests
- 15 unit tests using helm-unittest
- Tests verify: Pod structure, volume mounts, timeouts, image overrides, and global registry support

---

## [1.4.2] - 2025-01-23

### Fixed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Library chart used by Harness Helm charts.

- **[KEDA](docs/KEDA.md)** – Event-driven autoscaling (ScaledObject, TriggerAuthentication). ScaledObject sets HPA ownership transfer by default so it can safely coexist with the library HPA.
- **[Init Containers](docs/INIT_CONTAINERS.md)** – Reusable init container templates (wait-for-directory).
- **[Testing](docs/TESTING.md)** – How we test the library (template smoke tests + helm-unittest + CI).

## Publish
Expand Down
11 changes: 6 additions & 5 deletions ci/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ run_scenario() {
}

echo "Rendering scenarios..."
run_scenario "HPA" "${VALUES_DIR}/hpa.yaml"
run_scenario "PDB" "${VALUES_DIR}/pdb.yaml"
run_scenario "KEDA" "${VALUES_DIR}/keda.yaml"
run_scenario "Ingress" "${VALUES_DIR}/ingress.yaml"
run_scenario "VirtualService" "${VALUES_DIR}/virtualservice.yaml"
run_scenario "HPA" "${VALUES_DIR}/hpa.yaml"
run_scenario "PDB" "${VALUES_DIR}/pdb.yaml"
run_scenario "KEDA" "${VALUES_DIR}/keda.yaml"
run_scenario "Ingress" "${VALUES_DIR}/ingress.yaml"
run_scenario "VirtualService" "${VALUES_DIR}/virtualservice.yaml"
run_scenario "WaitForDirectory" "${VALUES_DIR}/wait-for-directory.yaml"
echo "All template scenarios passed."

echo ""
Expand Down
6 changes: 3 additions & 3 deletions ci/test-chart/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dependencies:
- name: harness-common
repository: file://../../src/common
version: 1.5.0
digest: sha256:07aa4a8f1ffa9232207093d17816bdc381cf9ca52eab319705ef6c184938f13c
generated: "2026-03-18T11:00:18.878124-04:00"
version: 1.5.1
digest: sha256:cb8865205b4aa522316e38d076bbc9afd4e059ee50300a59bd18aeeaea629d98
generated: "2026-03-27T16:08:03.521959-04:00"
2 changes: 1 addition & 1 deletion ci/test-chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ version: 0.0.1
appVersion: "0.0.1"
dependencies:
- name: harness-common
version: "1.5.0"
version: "1.5.1"
repository: "file://../../src/common"
7 changes: 7 additions & 0 deletions ci/test-chart/ci-values/wait-for-directory.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Enable wait-for-directory init container test
waitForDirectory:
enabled: true
directoryPath: /var/dumps
containerName: wait-for-dumps
timeout: 300
checkInterval: 2
25 changes: 25 additions & 0 deletions ci/test-chart/templates/wait-for-directory.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{- if .Values.waitForDirectory.enabled }}
apiVersion: v1
kind: Pod
metadata:
name: test-wait-for-directory
spec:
initContainers:
{{ include "harnesscommon.initContainer.waitForDirectory" (dict
"root" .
"directoryPath" .Values.waitForDirectory.directoryPath
"containerName" .Values.waitForDirectory.containerName
"image" .Values.waitForDirectory.image
"timeout" .Values.waitForDirectory.timeout
"checkInterval" .Values.waitForDirectory.checkInterval
) | nindent 4 }}
containers:
- name: main
image: busybox:1.36
command: ["sleep", "infinity"]
volumes:
- name: host-directory-check
hostPath:
path: {{ .Values.waitForDirectory.directoryPath }}
type: Directory
{{- end }}
122 changes: 122 additions & 0 deletions ci/test-chart/tests/wait_for_directory_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
suite: Wait For Directory Init Container
values:
- ../values.yaml
- ../ci-values/wait-for-directory.yaml
templates:
- wait-for-directory.yaml
release:
name: harness-common-test
namespace: default
tests:
- it: should render Pod with wait-for-directory init container
asserts:
- hasDocuments:
count: 1
- isKind:
of: Pod
- equal:
path: metadata.name
value: test-wait-for-directory

- it: should have init container with correct name
asserts:
- equal:
path: spec.initContainers[0].name
value: wait-for-dumps

- it: should have busybox image in init container
asserts:
- matchRegex:
path: spec.initContainers[0].image
pattern: busybox:1\.36$

- it: should have correct directory path in command
asserts:
- matchRegex:
path: spec.initContainers[0].args[0]
pattern: /var/dumps

- it: should have timeout value in script
asserts:
- matchRegex:
path: spec.initContainers[0].args[0]
pattern: "300"

- it: should have check interval in script
asserts:
- matchRegex:
path: spec.initContainers[0].args[0]
pattern: "sleep 2"

- it: should have volume mount for directory check
asserts:
- equal:
path: spec.initContainers[0].volumeMounts[0].name
value: host-directory-check
- equal:
path: spec.initContainers[0].volumeMounts[0].mountPath
value: /var/dumps
- equal:
path: spec.initContainers[0].volumeMounts[0].readOnly
value: true

- it: should have hostPath volume
asserts:
- equal:
path: spec.volumes[0].name
value: host-directory-check
- equal:
path: spec.volumes[0].hostPath.path
value: /var/dumps
- equal:
path: spec.volumes[0].hostPath.type
value: Directory

- it: should use custom container name when specified
asserts:
- equal:
path: spec.initContainers[0].name
value: wait-for-dumps
- matchRegex:
path: spec.initContainers[0].args[0]
pattern: "300" # default timeout
- matchRegex:
path: spec.initContainers[0].args[0]
pattern: "sleep 2" # default checkInterval

- it: should support custom image configuration
values:
- ../values.yaml # Only base values, no ci-values overlay
set:
waitForDirectory:
enabled: true
directoryPath: /custom/path
image:
registry: custom.registry.io
repository: custom-busybox
tag: "latest"
pullPolicy: Always
asserts:
- matchRegex:
path: spec.initContainers[0].image
pattern: custom\.registry\.io/custom-busybox:latest$
- equal:
path: spec.initContainers[0].imagePullPolicy
value: Always

- it: should respect global.imageRegistry override
values:
- ../values.yaml
set:
global:
imageRegistry: global-registry.io
waitForDirectory:
enabled: true
directoryPath: /test/path
image:
repository: busybox
tag: "1.36"
asserts:
- matchRegex:
path: spec.initContainers[0].image
pattern: ^global-registry\.io/busybox:1\.36$
3 changes: 3 additions & 0 deletions ci/test-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ virtualService:
objects: []
service:
port: 80
waitForDirectory:
enabled: false
directoryPath: ""
99 changes: 99 additions & 0 deletions docs/INIT_CONTAINERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Init Containers

The library chart provides reusable init container templates for common initialization patterns.

---

## Wait for Directory

Creates an init container that waits for a directory to exist on the Kubernetes node before starting the main container. Useful when pods need to ensure that host-mounted directories are available before initialization.

### Template

```yaml
{{- include "harnesscommon.initContainer.waitForDirectory" (dict
"root" .
"directoryPath" "/var/dumps"
"containerName" "wait-for-dumps"
"timeout" 300
) | nindent 8 }}
```

### Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `root` | Object | ✅ | - | Helm context scope (usually `.`) |
| `directoryPath` | String | ✅ | - | The directory path on the node to wait for |
| `containerName` | String | ❌ | `wait-for-directory` | Name of the init container |
| `image` | Object | ❌ | `{registry: "docker.io", repository: "busybox", tag: "1.36"}` | Container image configuration |
| `timeout` | Integer | ❌ | `300` | Maximum time in seconds to wait before failing |
| `checkInterval` | Integer | ❌ | `2` | Seconds between directory existence checks |

### Image Override

The template uses the standard `common.images.image` helper, so you can override the image globally or per-template:

```yaml
# Override globally for all images
global:
imageRegistry: my-registry.io

# Override per-template
waitForDirectory:
image:
registry: custom.registry.io
repository: custom-busybox
tag: "latest"
pullPolicy: Always
```

### Complete Example

```yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
initContainers:
{{ include "harnesscommon.initContainer.waitForDirectory" (dict
"root" .
"directoryPath" "/var/dumps"
"containerName" "wait-for-dumps"
"timeout" 600
"checkInterval" 5
) | nindent 4 }}
containers:
- name: main
image: my-app:latest
volumeMounts:
- name: dumps-data
mountPath: /data
volumes:
- name: host-directory-check
hostPath:
path: /var/dumps
type: Directory
- name: dumps-data
hostPath:
path: /var/dumps
type: Directory
```

### Important Notes

1. **Volume Mount Required**: The template expects a volume named `host-directory-check` to be defined in your pod spec, mounted to the same path as `directoryPath`.

2. **Read-Only Mount**: The init container mounts the directory as read-only for safety. It only checks for existence, not write access.

3. **Timeout Behavior**: If the directory doesn't exist within the timeout period, the init container will fail with exit code 1, preventing the main container from starting.

4. **Progress Logging**: The init container logs its progress every `checkInterval` seconds, showing elapsed time and timeout remaining.

### Use Cases

- Waiting for CSI drivers to mount volumes
- Ensuring NFS mounts are ready
- Waiting for local storage provisioners
- Coordinating with DaemonSets that prepare host directories
2 changes: 1 addition & 1 deletion src/common/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type: library
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.5.0
version: 1.5.1


# This is the version number of the application being deployed. This version number should be
Expand Down
Loading
Loading