helm-cel

module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 26, 2024 License: MIT

README

License Current Release GitHub Repo stars GitHub all releases GitHub issues GitHub pull requests codecov Artifact Hub

Helm CEL Plugin

A Helm plugin that uses Common Expression Language (CEL) to validate values. Instead of using JSON Schema in values.schema.json, you can write more expressive validation rules using CEL in values.cel.yaml.

Installation

helm plugin install https://github.com/idsulik/helm-cel

Usage

Create a values.cel.yaml file in your chart directory alongside your values.yaml file:

rules:
  - expr: "has(values.service) && has(values.service.port)"
    desc: "service port is required"
  
  - expr: "values.service.port >= 1 && values.service.port <= 65535"
    desc: "service port must be between 1 and 65535"
  
  - expr: "!(has(values.replicaCount)) || values.replicaCount >= 1"
    desc: "if replicaCount is set, it must be at least 1"

Then run validation:

helm cel /path/to/your/chart

Example

Given this values.yaml:

service:
  type: ClusterIP
  port: 80

replicaCount: 1

image:
  repository: nginx
  tag: latest

And this values.cel.yaml:

rules:
  - expr: "has(values.service) && has(values.service.port)"
    desc: "service port is required"
  
  - expr: "values.service.port >= 1 && values.service.port <= 65535"
    desc: "service port must be between 1 and 65535"
  
  - expr: "values.replicaCount >= 1"
    desc: "replica count must be at least 1"
  
  - expr: |
      has(values.image) && 
      has(values.image.repository) && 
      has(values.image.tag)
    desc: "image repository and tag are required"

If validation fails, you'll get a clear error message:

❌ Validation failed: replica count must be at least 1
   Rule: values.replicaCount >= 1
   Path: replicaCount
   Current value: 0

Writing Validation Rules

Each rule in values.cel.yaml consists of:

  • expr: A CEL expression that should evaluate to true for valid values
  • desc: A description of what the rule validates
  • severity: Optional severity level ("error" or "warning", defaults to "error")

The values.cel.yaml can also contain named expressions that can be reused across rules:

expressions:
  # Named expressions for reuse
  portRange: 'values.service.port >= 1 && values.service.port <= 65535'
  nodePortRange: 'values.service.nodePort >= 30000 && values.service.nodePort <= 32767'

rules:
  - expr: "${portRange}"
    desc: "Service port must be between 1 and 65535"
  
  - expr: 'values.service.type == "NodePort" ? ${nodePortRange} : true'
    desc: "NodePort must be between 30000 and 32767 when service type is NodePort"
  
  - expr: "values.replicaCount >= 2"
    desc: "Consider running multiple replicas for high availability"
    severity: warning
Severity Levels

Rules can have two severity levels:

  • error: Validation fails if the rule is not satisfied (default)
  • warning: Shows a warning but allows validation to pass

Example output with warnings:

Found 1 warning(s):

⚠️ Service port must be between 1 and 65535 and replica count must be within min and max bounds
   Rule: (values.service.port >= 1 && values.service.port <= 65535)  && (values.replicaCount >= values.minReplicas &&   values.replicaCount <= values.maxReplicas)
   Path: service.port
   Current value: 80801111111
-------------------------------------------------
⚠️✅ Values validation successful with warnings!
Common Validation Patterns
  1. Required fields:
- expr: "has(values.fieldName)"
  desc: "fieldName is required"
  1. Value constraints:
- expr: "values.number >= 0 && values.number <= 100"
  desc: "number must be between 0 and 100"
  1. Conditional requirements:
- expr: "!(has(values.optional)) || values.optional >= 0"
  desc: "if optional is set, it must be non-negative"
  1. Type validation:
- expr: "type(values.ports) == list"
  desc: "ports must be a list"
  1. Complex object validation:
- expr: |
    has(values.container) && 
    has(values.container.image) && 
    has(values.container.tag)
  desc: "container must have image and tag"
  1. Resource validation:
expressions:
  validateResources: '
    has(values.resources) &&
    has(values.resources.requests) &&
    has(values.resources.limits) &&
    matches(string(values.resources.requests.memory), r"^[0-9]+(Mi|Gi)$") &&
    matches(string(values.resources.limits.memory), r"^[0-9]+(Mi|Gi)$") &&
    matches(string(values.resources.requests.cpu), r"^[0-9]+m$|^[0-9]+$") &&
    matches(string(values.resources.limits.cpu), r"^[0-9]+m$|^[0-9]+$")
  '

rules:
  - expr: "${validateResources}"
    desc: "Resource requests and limits must be properly formatted"
  1. Complex service validation:
expressions:
  portRange: 'values.service.port >= 1 && values.service.port <= 65535'

rules:
  - expr: 'values.service.type in ["ClusterIP", "NodePort", "LoadBalancer"]'
    desc: "Service type must be valid"

  - expr: "${portRange}"
    desc: "Service port must be valid"

  - expr: '!values.ingress.enabled || has(values.ingress.className)'
    desc: "Ingress className should be specified when enabled"
    severity: warning
  1. Conditional validation with reusable expressions:
expressions:
  nodePortRange: 'values.service.nodePort >= 30000 && values.service.nodePort <= 32767'

rules:
  - expr: 'values.service.type == "NodePort" ? ${nodePortRange} : true'
    desc: "NodePort must be in valid range when service type is NodePort"

Development

Requirements:

  • Go 1.22 or later

Build:

make build

Install locally:

make install

Run tests:

make test

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

Distributed under the MIT License. See LICENSE for more information.

Directories

Path Synopsis
cmd
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL