Skip to content

bug: fix ephemeral container state when DATA_DIR is not set#15

Merged
nullable-eth merged 1 commit intomainfrom
fix-ephemeral-container
Jul 9, 2025
Merged

bug: fix ephemeral container state when DATA_DIR is not set#15
nullable-eth merged 1 commit intomainfrom
fix-ephemeral-container

Conversation

@nullable-eth
Copy link
Owner

Fix: Restore Ephemeral Mode When DATA_DIR is Not Set

🐛 Issue Summary

PR #9 introduced persistent storage as a major enhancement, but inadvertently broke the application's previous ephemeral behavior when DATA_DIR is not explicitly set. This caused the application to always attempt to create a /data directory, leading to permission errors in Docker containers and breaking backward compatibility.

Related Issues:

  • Issue #12 - Permission denied when creating /data directory

📋 Root Cause Analysis

What PR #9 Changed

PR #9 added several excellent features:

  • Persistent Storage - Tracks processed items across restarts
  • Radarr/Sonarr Integration - Automatic TMDb ID detection
  • Keyword Normalization - Intelligent formatting
  • Verbose Logging - Detailed debugging
  • Force Update Mode - Reprocess all items

The Problem

However, the persistent storage implementation made storage mandatory rather than optional:

// Before this PR - always tries to create storage
DataDir: getEnvWithDefault("DATA_DIR", "/data"),

// In NewProcessor - always initializes storage
stor, err := storage.NewStorage(cfg.DataDir)
if err != nil {
    return nil, fmt.Errorf("failed to initialize storage: %w", err)
}

This meant that even when users didn't set DATA_DIR, the application would:

  1. Default to /data directory
  2. Try to create it with os.MkdirAll(dataDir, 0755)
  3. Fail with permission errors in Docker containers running as non-root

The Error

❌ Failed to initialize processor: failed to initialize storage: failed to create data directory: mkdir /data: permission denied

🔧 Solution

This PR restores the ephemeral behavior when DATA_DIR is not set while preserving all the new functionality when it is set.

Changes Made

1. Optional Storage Configuration

// OLD - Always defaults to /data
DataDir: getEnvWithDefault("DATA_DIR", "/data"),

// NEW - Only set when explicitly provided
DataDir: os.Getenv("DATA_DIR"), // No default - ephemeral if not set

2. Conditional Storage Initialization

// NEW - Only create storage if DataDir is set
var stor *storage.Storage
if cfg.DataDir != "" {
    var err error
    stor, err = storage.NewStorage(cfg.DataDir)
    if err != nil {
        return nil, fmt.Errorf("failed to initialize storage: %w", err)
    }
}

3. Conditional Storage Operations

// NEW - Check if storage exists before using it
if p.storage != nil {
    if processed, storageExists := p.storage.Get(item.GetRatingKey()); storageExists && processed.KeywordsSynced && processed.UpdateField == p.config.UpdateField && !p.config.ForceUpdate {
        // Skip if already processed
        continue
    }
}

// NEW - Only save if storage is enabled
if p.storage != nil {
    if err := p.storage.Set(processedItem); err != nil {
        fmt.Printf("⚠️ Warning: Failed to save processed item to storage: %v\n", err)
    }
}

4. Clear User Feedback

// NEW - Clear messaging about storage mode
if stor != nil {
    count := stor.Count()
    if count > 0 {
        fmt.Printf("📁 Loaded %d previously processed items from storage\n", count)
    }
} else {
    fmt.Printf("🔄 Running in ephemeral mode - no persistent storage (set DATA_DIR to enable)\n")
}

📊 Behavior Comparison

Before This Fix

DATA_DIR Set Behavior Result
❌ Not set Always tries to create /data ❌ Permission error
✅ Set to path Creates storage at path ✅ Persistent storage

After This Fix

DATA_DIR Set Behavior Result
❌ Not set Runs ephemerally ✅ Processes all items every run
✅ Set to path Creates storage at path ✅ Persistent storage

🧪 Testing

Build Success

go build -o labelarr ./cmd/labelarr
# ✅ Builds successfully

Test Results

go test ./...
# ✅ All tests pass

Runtime Behavior

Without DATA_DIR:

🔄 Running in ephemeral mode - no persistent storage (set DATA_DIR to enable)

With DATA_DIR:

📁 Loaded 1250 previously processed items from storage

🔄 Migration Guide

For Users Running Ephemerally (No Change Required)

# This will now work without permission errors
services:
  labelarr:
    image: ghcr.io/nullable-eth/labelarr:latest
    environment:
      - PLEX_TOKEN=your_token
      - TMDB_READ_ACCESS_TOKEN=your_token
      # No DATA_DIR needed - runs ephemerally

For Users Wanting Persistent Storage

# Add DATA_DIR and volume mount
services:
  labelarr:
    image: ghcr.io/nullable-eth/labelarr:latest
    volumes:
      - ./labelarr-data:/data
    environment:
      - PLEX_TOKEN=your_token
      - TMDB_READ_ACCESS_TOKEN=your_token
      - DATA_DIR=/data  # Enable persistent storage

🎯 Benefits

  1. ✅ Backward Compatibility - Restores ephemeral behavior when DATA_DIR not set
  2. ✅ No Permission Errors - No more Docker permission issues
  3. ✅ Clear User Control - Explicit opt-in for persistent storage
  4. ✅ Preserves All Features - Radarr/Sonarr, normalization, verbose logging all work
  5. ✅ Graceful Degradation - Application works with or without storage

🚀 Impact

This fix resolves the breaking change introduced in PR #9 while preserving all the valuable new functionality. Users can now:

  • Run the application without any storage concerns (ephemeral mode)
  • Explicitly enable persistent storage when desired
  • Migrate from ephemeral to persistent operation seamlessly

The application now behaves correctly in both modes, restoring the flexibility that existed before PR #9 while maintaining all the new capabilities.

when DATA_DIR not set
@nullable-eth nullable-eth merged commit 7a2c0b6 into main Jul 9, 2025
@nullable-eth nullable-eth deleted the fix-ephemeral-container branch July 9, 2025 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant