Skip to content

b1_06_tekton_bundle_tracking_

Simon Baird edited this page Apr 23, 2025 · 1 revision

Chapter 6: Tekton Bundle Tracking

In the previous chapter, we saw how ec-cli acts like a skilled librarian, finding and fetching container images and related artifacts (Image & OCI Artifact Handling). This is crucial for checking your application's components.

But applications aren't just built from container images; they are often built by automated pipelines. In the Kubernetes world, Tekton is a popular tool for creating these pipelines. Tekton pipelines are made up of Tasks (reusable steps) and Pipelines (sequences of Tasks). These Tasks and Pipelines can be packaged together into Tekton Bundles, which are stored in container registries just like container images!

How do we make sure our build pipelines are using the right, approved, and up-to-date versions of these Tekton Tasks?

What Problem Does it Solve? Managing Trusted Pipeline Steps

Imagine your organization uses a standard Tekton Task for building container images, let's call it quay.io/my-org/build-task:v1. You want to ensure all your CI/CD pipelines use only approved versions of this task. Maybe v1 is okay for now, but you plan to release v2 next month with important security fixes. How do you:

  1. Keep a record of which versions (v1, v2) are considered "trusted"?
  2. Know when it's time to encourage teams to update from v1 to v2?
  3. Allow ec-cli policies to check if a pipeline run actually used a trusted task version?

This is where Tekton Bundle Tracking comes in. It acts like a subscription manager for your Tekton Tasks and Pipelines.

Think of it this way: You subscribe to a magazine. The publisher keeps a list of active subscribers and knows when your subscription is about to expire, prompting you to renew. Similarly, Tekton Bundle Tracking keeps a list of "trusted" Tekton bundle versions (by their unique digest) and sets an "effective date" to encourage updates.

How it Works: Creating a Trust List

The core idea is to generate a data file (usually YAML) that lists the specific Tekton bundles (identified by their unique digest, like a fingerprint) that your organization trusts.

Here's the basic flow:

  1. You tell ec-cli which bundles to track: You use the ec track bundle command, pointing it to the Tekton bundles you want to manage (e.g., quay.io/my-org/build-task:v1).
  2. ec-cli finds the exact version: Just like with container images (Chapter 5), ec-cli resolves the tag (:v1) to its unique digest (sha256:...).
  3. ec-cli updates a tracking file: It adds an entry to a YAML file (let's call it trusted_tasks.yml). This entry includes:
    • The bundle's repository and tag/digest.
    • The specific digest (ref) that is trusted.
    • An effective_on date (e.g., 30 days from now). This date signifies when this version becomes the standard, encouraging updates to older versions before this date.
  4. Policies use this file: Later, when ec-cli validates a pipeline run, policies (Chapter 2) can consult this trusted_tasks.yml file. They check if the specific Tekton Task digest used in the pipeline run is listed in the file and if its effective_on date has passed (or is the most recent one).

This process ensures that you have a clear, manageable record of trusted Tekton resources and a mechanism to drive updates.

Using ec track bundle

The main way to interact with this feature is through the ec track bundle command (which also has an alias ec track tekton-task).

Example 1: Tracking a new bundle

Let's say you want to start tracking quay.io/my-org/build-task:v1.

# Tell ec-cli to track this Tekton bundle
ec track bundle --bundle quay.io/my-org/build-task:v1

Explanation:

  • ec track bundle: The command to manage Tekton bundle tracking.
  • --bundle quay.io/my-org/build-task:v1: Specifies the Tekton bundle image reference to add to the tracking list.

ec-cli will:

  1. Connect to quay.io.
  2. Find the digest for the :v1 tag (e.g., sha256:abc...def).
  3. Calculate an effective_on date (e.g., 30 days from today).
  4. Print the tracking data to your console (standard output).

The output might look something like this (simplified):

# Output printed to console
trusted_tasks:
  oci://quay.io/my-org/build-task:v1:          # The bundle reference being tracked
    - effective_on: "2024-09-15T00:00:00Z"     # Date when this entry becomes effective
      ref: sha256:abcdef1234567890abcdef...   # The trusted digest for v1

Explanation of Output:

  • trusted_tasks: The main section listing tracked bundles.
  • oci://quay.io/my-org/build-task:v1: The key identifying the bundle group (using the oci:// prefix).
  • - effective_on: The date this specific digest (ref) is considered the standard. Policies might warn about older versions before this date.
  • ref: The unique, immutable digest of the bundle version that is trusted.

Example 2: Updating an existing tracking file

Usually, you'll want to save this data to a file and update it over time.

# Create an initial file
ec track bundle --bundle quay.io/my-org/build-task:v1 --output trusted_tasks.yml

# Later, track a new version (v2) and add it to the existing file
ec track bundle --bundle quay.io/my-org/build-task:v2 --input trusted_tasks.yml --output trusted_tasks.yml

Explanation:

  • --output trusted_tasks.yml: Saves the output to a file named trusted_tasks.yml.
  • --input trusted_tasks.yml: Reads the existing tracking data from trusted_tasks.yml before adding new entries.

After adding v2 (let's say its digest is sha256:123...456), the trusted_tasks.yml file might look like this:

# trusted_tasks.yml after adding v2
trusted_tasks:
  oci://quay.io/my-org/build-task:v1:
    - effective_on: "2024-09-15T00:00:00Z" # v1 effective date
      ref: sha256:abcdef1234567890abcdef...
  oci://quay.io/my-org/build-task:v2:
    - effective_on: "2024-10-15T00:00:00Z" # v2 effective date (e.g., 30 days after adding)
      ref: sha256:1234567890abcdef123456...

Other Useful Flags:

  • --prune (default: true): Automatically removes older entries that are no longer considered relevant (e.g., if a newer version is already effective).
  • --freshen: If you track a bundle using a tag (like :latest), this flag tells ec-cli to re-check the registry for the current digest associated with that tag and add a new entry if it has changed. This helps keep track of rolling tags.
  • --git git+https://...: You can also track Tekton Task files directly from Git repositories.
  • --replace: Updates the --input file directly instead of requiring --output.
  • --in-effect-days N: Sets the number of days in the future for the effective_on date (default is 30).

Under the Hood: Tracking Logic

How does ec track bundle build this list?

sequenceDiagram
    participant User
    participant EC_CLI as ec track bundle
    participant Parser as Argument Parser
    participant OCI_Handler as OCI/Git Handler
    participant TrackerLogic as Tracker Logic
    participant OutputHandler as Output Handler

    User->>EC_CLI: Runs `ec track bundle --bundle <ref> [--input file] [--output file]`
    EC_CLI->>Parser: Parse flags (--bundle, --input, --output etc.)
    Parser-->>EC_CLI: Parsed arguments
    Note over EC_CLI: Has list of bundles to track & input/output paths
    EC_CLI->>OutputHandler: Read data from --input file (if provided)
    OutputHandler-->>EC_CLI: Existing tracking data (or empty)
    EC_CLI->>OCI_Handler: For each --bundle <ref>, resolve digest (e.g., sha256:abc...)
    Note over OCI_Handler: Uses logic similar to Chapter 5
    OCI_Handler-->>EC_CLI: Resolved digest for <ref>
    EC_CLI->>TrackerLogic: Add/Update record (bundle ref, digest, effective_on) in tracking data
    TrackerLogic->>TrackerLogic: Apply pruning/freshening logic if needed
    TrackerLogic-->>EC_CLI: Updated tracking data
    EC_CLI->>OutputHandler: Write updated data to --output file (or stdout)
    OutputHandler-->>User: Shows output / Saves file
Loading

Code Snippets:

  1. Command Setup (cmd/track/track_bundle.go) This file sets up the ec track bundle command, defines its flags (--bundle, --input, --output, --prune, --freshen, etc.), and calls the core tracking logic.

    // Simplified view from cmd/track/track_bundle.go
    func trackBundleCmd(track trackBundleFn /*...*/) *cobra.Command {
        params := struct { // Structure to hold flag values
            bundles      []string
            input        string
            prune        bool
            // ... other flags
        }{ prune: true /* default */ }
    
        cmd := &cobra.Command{
            Use:   "bundle",
            Short: "Record tracking information about Tekton bundles",
            RunE: func(cmd *cobra.Command, args []string) (err error) {
                // ... Read input data from params.input (file or OCI) ...
                var data []byte
                if params.input != "" { /* read data */ }
    
                // *** Call the core tracking function ***
                out, err := track(cmd.Context(), params.bundles, data,
                                 params.prune, params.freshen, /*...*/)
                if err != nil { return err }
    
                // ... Write output data 'out' based on params.output ...
    
                return nil
            },
        }
        // Define flags using cmd.Flags()...
        cmd.Flags().StringSliceVarP(&params.bundles, "bundle", "b", /*...*/)
        cmd.Flags().StringVarP(&params.input, "input", "i", /*...*/)
        // ... other flags defined ...
        return cmd
    }

    Explanation: This sets up the command-line interface. It defines the flags users can provide and then, in RunE, reads any input, calls the main track function (passing the flag values), and handles writing the output.

  2. Core Tracking Logic (internal/tracker/tracker.go) This is where the main work happens: reading existing data, resolving bundles, adding records, and applying filtering.

    // Represents one entry in the tracking list for a specific digest
    type taskRecord struct {
        Ref         string    `json:"ref"` // The trusted digest (sha256:...)
        EffectiveOn time.Time `json:"effective_on"` // When it becomes effective
        // ... other fields like ExpiresOn ...
        Tag        string     `json:"-"` // Transient info, not saved in YAML
        Repository string     `json:"-"` // Transient info
    }
    
    // Holds the entire tracking data, mapping bundle group to records
    type Tracker struct {
        TrustedTasks map[string][]taskRecord `json:"trusted_tasks,omitempty"`
    }
    
    // Main function called by the command
    func Track(ctx context.Context, urls []string, input []byte, prune bool, freshen bool, /*...*/) ([]byte, error) {
        // Load existing data from 'input' bytes
        t, err := newTracker(input)
        if err != nil { return nil, err }
    
        // Process image URLs (e.g., quay.io/...)
        if err := t.trackImageReferences(ctx, imageUrls, freshen, /*...*/); err != nil {
            // ... handle error ...
        }
        // Process git URLs (e.g., git+https://...)
        if err := t.trackGitReferences(ctx, gitUrls, freshen, /*...*/); err != nil {
            // ... handle error ...
        }
    
        // Apply filtering/pruning rules
        t.filterBundles(prune)
        // Set expiration dates based on effective dates
        t.setExpiration()
    
        // Convert the final Tracker struct back to YAML bytes
        return t.Output()
    }

    Explanation: The Track function orchestrates the process. It parses the input YAML into a Tracker struct. It calls helper functions (trackImageReferences, trackGitReferences) which resolve the bundle URLs to digests (using code similar to Chapter 5) and add taskRecord entries. It then filters these records (filterBundles) and finally converts the updated Tracker struct back into YAML for output.

  3. Checking Bundle Contents (internal/tracker/bundle_info.go) ec-cli needs to check if a bundle actually contains a Tekton Task before tracking it.

    // Simplified concept: Checks if a bundle manifest lists a layer as a "task"
    func containsTask(ctx context.Context, ref image.ImageReference) (bool, error) {
        // Get image manifest using OCI client (like in Chapter 5)
        img, err := /* ... get image from registry ... */
        if err != nil { return false, err }
        manifest, err := img.Manifest()
        if err != nil { return false, err }
    
        // Check annotations on layers in the manifest
        for _, layer := range manifest.Layers {
            // Tekton bundles use annotations to mark layer types
            if kind, ok := layer.Annotations["dev.tekton.image.kind"]; ok {
                if kind == "task" { // Found a layer marked as a Tekton Task!
                    return true, nil
                }
            }
        }
        return false, nil // No task layer found
    }

    Explanation: This function examines the bundle's manifest file (metadata stored in the registry). It looks for specific annotations that Tekton uses to identify layers containing Tasks. If it finds one, it confirms the bundle is suitable for tracking.

How Policies Use the Tracking Data

The trusted_tasks.yml file generated by ec track bundle becomes a crucial piece of data for your policies (Chapter 2). When ec-cli validates a system (like a Kubernetes cluster where pipelines run), Rego policies executed by the Validation Engine (Evaluator) can:

  1. Identify which Tekton Task (by its digest) was used in a specific pipeline run.
  2. Load the trusted_tasks.yml data.
  3. Check if the digest used in the pipeline run is present in the trusted_tasks list.
  4. Potentially check the effective_on date to enforce updates.

This allows you to create rules like: "Pipeline runs must only use Tekton Task digests that are listed as trusted and currently effective."

Conclusion

You've learned about Tekton Bundle Tracking, ec-cli's feature for managing the trustworthiness and lifecycle of your Tekton Tasks and Pipelines. It acts like a subscription manager, creating a data file (trusted_tasks.yml) that lists approved bundle digests and their effective dates using the ec track bundle command. This helps you ensure your CI/CD pipelines use verified components and encourages timely updates. This generated data file is then used by policies during validation.

We've now covered how ec-cli defines inputs, sources policies, evaluates rules, verifies signatures, handles artifacts, and tracks Tekton bundles. The final piece of the puzzle is understanding how ec-cli presents the results of all these checks back to you.

Let's move on to the final chapter: Output Reporting.


Generated by AI Codebase Knowledge Builder

Clone this wiki locally