Skip to content

mkm29/valet

Repository files navigation

Valet: Helm Values to JSON Schema

Valet Logo

Fast. Flexible. Clean. Beautiful.

Release Coverage

A command-line tool to generate a JSON Schema from a YAML values.yaml file, optionally merging an overrides file. Useful for Helm chart values and other YAML-based configurations.

Table of Contents

Overview

Valet automatically generates JSON Schema definitions from Helm chart values.yaml files:

  • Infers types from YAML values
  • Preserves defaults from your values files
  • Handles components with enabled flags intelligently
  • Supports overrides via separate YAML files
  • Speeds up development by providing schema validation for Helm charts
  • Beautiful CLI experience powered by Charm's Fang library

Architecture

%%{
  init: {
    'theme': 'dark',
    'themeVariables': {
      'primaryColor': '#3e4451',
      'primaryTextColor': '#abb2bf',
      'primaryBorderColor': '#56b6c2',
      'lineColor': '#61afef',
      'secondaryColor': '#2c313c',
      'tertiaryColor': '#3b4048'
    }
  }
}%%
graph TD
    Main[main.go] --> |entry point| Cmd[cmd package]
    Cmd --> RootCmd[cmd/root.go]
    RootCmd --> GenerateCmd[cmd/generate.go]
    RootCmd --> VersionCmd[cmd/version.go]
    GenerateCmd --> Config[internal/config]
    GenerateCmd --> |schema generation| SchemaGen[Schema Generator]
    GenerateCmd --> Telemetry[internal/telemetry]
    Config --> |config loading| YAML[YAML Config Files]

    subgraph "Core Functionality"
        SchemaGen --> TypeInference[Type Inference]
        SchemaGen --> ComponentHandling[Component Processing]
        SchemaGen --> OverrideMerging[Override Merging]
    end

    subgraph "CLI Interface"
        Main --> |wrapped by| Fang[Fang CLI Framework]
        Fang --> Cmd
        Cmd
        RootCmd
        GenerateCmd
        VersionCmd
    end

    subgraph "Configuration"
        Config
        YAML
    end

    subgraph "Observability"
        Telemetry --> Tracing[OpenTelemetry Tracing]
        Telemetry --> Metrics[Metrics Collection]
        Telemetry --> Logging[Structured Logging]
        Tracing --> OTLP[OTLP Exporter]
        Metrics --> OTLP
        Logging --> |zap integration| Tracing
    end

    classDef core fill:#c678dd,stroke:#61afef,stroke-width:1px,color:#efefef;
    classDef cli fill:#61afef,stroke:#56b6c2,stroke-width:1px,color:#efefef;
    classDef config fill:#98c379,stroke:#56b6c2,stroke-width:1px,color:#282c34;
    classDef fang fill:#e06c75,stroke:#56b6c2,stroke-width:2px,color:#efefef;
    classDef telemetry fill:#56b6c2,stroke:#61afef,stroke-width:1px,color:#efefef;

    class SchemaGen,TypeInference,ComponentHandling,OverrideMerging core;
    class Main,Cmd,RootCmd,GenerateCmd,VersionCmd cli;
    class Config,YAML config;
    class Fang fang;
    class Telemetry,Tracing,Metrics,Logging,OTLP telemetry;
Loading

Installation

From Source

Clone the repository and build:

git clone https://github.com/mkm29/valet.git
cd valet
go build -o bin/valet main.go

Using Go Install

Install directly using Go modules:

go install github.com/mkm29/valet@latest

Usage

Generate a JSON Schema from a values.yaml in the given <context-dir> using the generate command:

valet [global options] generate [flags] <context-dir>

Global options:
  --config-file string          config file path (default: .valet.yaml)
  -d, --debug                   enable debug logging
  --telemetry-enabled           enable telemetry
  --telemetry-exporter string   telemetry exporter type (none, stdout, otlp) (default: none)
  --telemetry-endpoint string   OTLP endpoint for telemetry (default: localhost:4317)
  --telemetry-insecure          use insecure connection for OTLP (default: false)
  --telemetry-sample-rate float trace sampling rate (0.0 to 1.0) (default: 1.0)

Generate flags:
  -f, --overrides string   path (relative to context dir) to an overrides YAML file (optional)
  -o, --output string      output file (default: values.schema.json)

The tool writes a values.schema.json (or custom output file) in the <context-dir>.

Configuration

Valet supports configuration through multiple sources, with precedence in the following order:

  1. CLI flags (highest priority)
  2. Environment variables
  3. Configuration file
  4. Default values (lowest priority)

Configuration File

The CLI supports a YAML configuration file (default: .valet.yaml) in the current directory. Use the --config-file flag to specify a custom path. The following keys are supported:

  • context: directory containing values.yaml
  • overrides: path to an overrides YAML file
  • output: name of the output schema file (default: values.schema.json)
  • debug: enable debug logging (boolean)
  • telemetry: telemetry configuration (object)
    • enabled: enable telemetry (boolean)
    • serviceName: service name for telemetry (default: valet)
    • serviceVersion: service version for telemetry (default: 0.1.0)
    • exporterType: type of exporter (none, stdout, otlp)
    • otlpEndpoint: OTLP endpoint for traces and metrics
    • insecure: use insecure connection for OTLP
    • sampleRate: trace sampling rate (0.0 to 1.0)
    • headers: additional headers for OTLP requests (map)

Environment Variables

Configuration can also be set via environment variables:

  • VALET_CONTEXT
  • VALET_OVERRIDES
  • VALET_OUTPUT
  • VALET_DEBUG

Examples

Generate schema from a directory containing values.yaml:

./bin/valet generate charts/mychart

Generate schema merging an override file:

./bin/valet generate --overrides override.yaml charts/mychart

Print version/build information:

./bin/valet version
github.com/mkm29/valet@v0.1.1 (commit 9153c14b9ffddeaccba93268a0851d5da0ae8cbf)

Observability

Valet includes comprehensive observability capabilities through OpenTelemetry integration, providing distributed tracing, metrics, and structured logging for monitoring and debugging.

Telemetry Configuration

Enable telemetry using CLI flags or configuration:

# Enable with stdout exporter (for development)
valet generate --telemetry-enabled --telemetry-exporter stdout charts/mychart

# Enable with OTLP exporter (for production)
valet generate --telemetry-enabled --telemetry-exporter otlp \
  --telemetry-endpoint localhost:4317 \
  --telemetry-insecure charts/mychart

Configuration Options

Telemetry can be configured via:

  1. CLI Flags:

    • --telemetry-enabled: Enable telemetry (default: false)
    • --telemetry-exporter: Exporter type: none, stdout, otlp (default: none)
    • --telemetry-endpoint: OTLP endpoint (default: localhost:4317)
    • --telemetry-insecure: Use insecure connection for OTLP (default: false for better security)
    • --telemetry-sample-rate: Trace sampling rate 0.0-1.0 (default: 1.0)
  2. Configuration File (.valet.yaml):

telemetry:
  enabled: true
  serviceName: valet
  exporterType: otlp
  otlpEndpoint: localhost:4317
  insecure: false
  sampleRate: 1.0
  headers:
    api-key: your-api-key
  1. Environment Variables:
    • VALET_TELEMETRY
    • VALET_TELEMETRY_EXPORTER
    • VALET_TELEMETRY_ENDPOINT
    • VALET_TELEMETRY_INSECURE
    • VALET_TELEMETRY_SAMPLE_RATE

Distributed Tracing

Valet creates detailed traces for all operations:

  • Command execution: Root span for the entire command
  • File operations: Loading values.yaml, overrides, writing schema
  • Schema generation: Type inference, merging, validation
  • Component processing: Individual spans for complex operations

Example trace structure:

generate.command
├── load.values_yaml
├── load.overrides_yaml (if applicable)
├── merge.yaml_files
├── generate.schema
├── marshal.json
└── write.schema_file

Metrics

The following metrics are collected:

  • Command Metrics:

    • valet.command.executions: Total command executions (counter)
    • valet.command.duration: Command execution duration (histogram)
    • valet.command.errors: Total command errors (counter)
  • File Operation Metrics:

    • valet.file.reads: File read operations (counter)
    • valet.file.writes: File write operations (counter)
    • valet.file.size: File size distribution (histogram)
  • Schema Generation Metrics:

    • valet.schema.generations: Total schema generations (counter)
    • valet.schema.fields: Number of fields in schemas (histogram)
    • valet.schema.generation_duration: Schema generation time (histogram)

All file path attributes in metrics are sanitized to protect sensitive information - only the filename and immediate parent directory are included in telemetry data.

Structured Logging

Valet uses Uber's zap for high-performance structured logging with OpenTelemetry integration:

  • Zero-allocation logging: Zap's design ensures minimal performance overhead
  • Structured fields: All log data is structured for easy parsing and querying
  • OpenTelemetry integration: Log entries automatically include trace and span IDs
  • Span events: All logs are also recorded as events in the active span
  • Level control: Info level by default, Debug level when --debug flag is set
  • JSON encoding: Logs are emitted as JSON for compatibility with log aggregation systems

Example log output:

{
  "timestamp": "2024-01-20T10:15:30.123Z",
  "level": "debug",
  "logger": "valet",
  "caller": "generate.go:459",
  "message": "Original YAML values loaded",
  "trace_id": "7d3e8f9a1b2c3d4e5f6a7b8c9d0e1f2a",
  "span_id": "1a2b3c4d5e6f7890",
  "file": "charts/mychart/values.yaml",
  "top_level_keys": 15
}

Integration with Observability Platforms

Valet's OTLP exporter can send telemetry data to any OpenTelemetry-compatible backend:

  • Jaeger: For distributed tracing
  • Prometheus: For metrics collection
  • Grafana: For visualization
  • Elastic APM: For application performance monitoring
  • New Relic, Datadog, etc.: Via OTLP support

Example docker-compose setup for local observability:

services:
  otel-collector:
    image: otel/opentelemetry-collector:latest
    ports:
      - "4317:4317"  # OTLP gRPC
      - "4318:4318"  # OTLP HTTP
    volumes:
      - ./otel-config.yaml:/etc/otel-collector-config.yaml
    command: ["--config=/etc/otel-collector-config.yaml"]

  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686"  # Jaeger UI
      - "14250:14250"  # Jaeger gRPC

Example Input/Output

Given a values.yaml:

replicaCount: 3
image:
  repository: nginx
  tag: stable
env:
  - name: LOG_LEVEL
    value: debug

Running the generate command:

./bin/valet generate .

Produces values.schema.json with contents:

{
  "$schema": "http://json-schema.org/schema#",
  "type": "object",
  "properties": {
    "replicaCount": {
      "type": "integer",
      "default": 3
    },
    "image": {
      "type": "object",
      "properties": {
        "repository": {
          "type": "string",
          "default": "nginx"
        },
        "tag": {
          "type": "string",
          "default": "stable"
        }
      },
      "default": {}
    },
    "env": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "default": "LOG_LEVEL"
          },
          "value": {
            "type": "string",
            "default": "debug"
          }
        },
        "default": {}
      },
      "default": []
    }
  },
  "required": ["replicaCount", "image", "env"]
}

How it works

  1. Load configuration from the file specified by --config-file (default: .valet.yaml), environment variables, and CLI flags
  2. Load values.yaml in the specified directory
  3. Merge an overrides YAML if the --overrides flag is provided
  4. Recursively infer JSON Schema types and defaults
  5. Post-process the schema to intelligently handle:
    • Components with enabled: false field (skipping required fields)
    • Empty default values (strings, arrays, maps)
    • Nested component structures
  6. Write values.schema.json (or custom output file) in the same directory

Schema Generation Intelligence

The tool includes several smart features:

  • Component detection: Automatically detects components with an enabled field and handles their required fields intelligently
  • Empty value handling: Fields with empty default values aren't marked as required
  • Type conversion: Maps and complex types are properly represented in the schema
  • Nested processing: Recursively processes properties at all levels of nesting

Development

Requirements

  • Go 1.23 or later

Makefile

A Makefile is provided with common development tasks:

  • make help: Show available commands (default when running make).
  • make build: Build the CLI (outputs bin/valet).
  • make test: Run tests, generate cover.out and cover.html.
  • make check-coverage: Install and run go-test-coverage to enforce coverage thresholds defined in .testcoverage.yml.
  • make clean: Remove build artifacts (bin/ and valet).

Make sure you have GNU Make installed.

Testing & Coverage

The project uses Testify as its testing framework, with all tests organized in the tests directory using the ValetTestSuite test suite.

You can use the Makefile to run tests and check coverage:

make test
make check-coverage

To run the test suite:

go test ./...

To run tests with verbose output:

go test ./tests/... -v

To generate a coverage report:

go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

To view an HTML coverage report:

go tool cover -html=coverage.out

Test Organization

All tests are located in the tests directory and use the ValetTestSuite struct which provides:

  • Setup and teardown functionality
  • Helper methods like CopyDir for test fixtures
  • Consistent assertion methods via Testify

The project maintains high test coverage standards:

  • 70% minimum coverage for each file
  • 80% minimum coverage for each package
  • 85% minimum total coverage

These thresholds are enforced in CI via the coverage workflow.

Release

This project uses GoReleaser to automate builds and releases. Binaries for Linux and macOS (amd64 and arm64) are built when tags (e.g., v0.1.0) are pushed.

  • A GitHub Actions workflow (.github/workflows/release.yml) runs GoReleaser on push tags and via manual dispatch.

  • Note: The release workflow sets permissions.contents: write so that the GITHUB_TOKEN has sufficient permissions to create releases.

  • To run a local release:

    go install github.com/goreleaser/goreleaser@latest
    goreleaser release --rm-dist

Contributing

Contributions are welcome! Feel free to open issues and submit pull requests.

About

Values YAML Schema Tool

Resources

Stars

Watchers

Forks

Packages

No packages published