Schedule icon
Download icon
WorkingDirectory icon
Script icon
Switch icon
UploadFiles icon
Set icon
Log icon
If icon
SlackIncomingWebhook icon
Fail icon

Release a dataset only when it meets its data contract

Enforce an ODCS data contract with datacontract-cli in Kestra. Block breaking changes, test each batch, publish data and contract docs. Runs with no setup.

Categories
Data

A data contract states what consumers can rely on: the columns, their types, their constraints and quality rules. This flow enforces one with the open-source Data Contract CLI before a batch is published. Each run does three checks:

  • It lints the contract against the Open Data Contract Standard (ODCS).
  • It compares the contract with the last published one. A breaking change is only accepted with a new major version, so consumers are never broken by a minor release.
  • It tests the batch against the contract.

Only when all three pass are the data and the HTML contract docs published, and the contract becomes the new baseline.

It runs with no setup. The contract is inline, the batch is a public CSV of 100 orders, and the output goes to namespace files. Run it with inject_bad_rows: true to watch a release get blocked.

This blueprint was created by zkasuran.

How it works

  1. download_batch (io.kestra.plugin.core.http.Download) fetches the CSV.
  2. verify (io.kestra.plugin.core.flow.WorkingDirectory) writes three files: the contract datacontract.yaml, previous.yaml from the KV store (empty on the first run) and the batch. check_contract (io.kestra.plugin.scripts.python.Script, datacontract-cli[duckdb]==1.2.2) then:
    • lints the contract,
    • runs breaking against previous.yaml, and flags breaking entries when the major version did not increase,
    • runs test against the local server with DuckDB (presence, required, unique, regex, minimum, a SQL rule and a row count rule),
    • exports the contract to contract.html,
    • outputs verdict (PASSED, INVALID_CONTRACT, BREAKING_CHANGE or DATA_FAILED) and the reasons.
  3. release (io.kestra.plugin.core.flow.Switch):
    • PASSED: publish (io.kestra.plugin.core.namespace.UploadFiles) writes data/published/orders.csv and contracts/orders/index.html. record_contract (io.kestra.plugin.core.kv.Set) stores the contract as the next baseline.
    • Any other verdict: block_release (io.kestra.plugin.core.execution.Fail) ends the run as FAILED with the reasons. The published data, docs and baseline stay as they were.
  4. The nightly Schedule runs it every day. Slack is optional on both paths.

Inputs

  • source_url (URI, default the public orders CSV): the batch.
  • inject_bad_rows (BOOL, default false): add a duplicate id, a negative price, a wrong total and an invalid email.
  • notify_slack (BOOL, default false): post the result to Slack.

Prerequisites

  • A Kestra worker that can run Docker containers. The script uses python:3.12-slim and installs the pinned CLI on each run, which takes about 30 seconds.

Secrets

  • SLACK_WEBHOOK_URL: Slack incoming webhook. Only needed when notify_slack is true.

Quick start

  1. Run the flow. 20 checks pass, the data and the contract docs appear in the namespace Files tab, and contract v1.0.0 becomes the baseline.
  2. Run it with inject_bad_rows: true. The run fails with DATA_FAILED and names the 4 failing checks.
  3. Edit the contract in verify: delete the customer_email property and set version: 1.1.0. The run fails with BREAKING_CHANGE. Set version: 2.0.0 and it is released as the new baseline.

Expected outputs

  • outputs.check_contract.vars.verdict plus lint_errors, breaking, failed_checks and checks.
  • Namespace files data/published/orders.csv and contracts/orders/index.html.
  • KV datacontract_orders_published: the contract last released.
  • outputs.verify.outputFiles['results.json']: the full test report.

Things to know

  • The CLI counts a new quality rule as a breaking change, because a stricter rule can fail data that passed before. Release it with a major version or relax it.
  • Every property in the contract must exist in the data. An extra column in the data is allowed.

Using it with your own data

  • Keep the contract in Git and sync it to namespace files with io.kestra.plugin.git.SyncNamespaceFiles, then pass it to verify with namespaceFiles instead of the inline file.
  • Change the servers entry to Postgres, Snowflake, BigQuery, Databricks or S3 and add the matching CLI extra, for example datacontract-cli[postgres]. Credentials go in env with {{ secret('NAME') }}.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.