Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 126 additions & 1 deletion docs/user-guide/commands/show.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ Provides a quick, read-only view of environment details including state, infrast
## Command Syntax

```bash
torrust-tracker-deployer show <ENVIRONMENT>
torrust-tracker-deployer show <ENVIRONMENT> [OPTIONS]
```

## Arguments

- `<ENVIRONMENT>` (required) - Name of the environment to display

## Options

- `-o, --output-format <FORMAT>` (optional) - Output format: `text` (default) or `json`

## Prerequisites

1. **Environment exists** - Must create environment first with `create environment`
Expand Down Expand Up @@ -91,6 +95,96 @@ Tracker Services:
Tracker is running! Use the URLs above to connect.
```

## Output Formats

The `show` command supports two output formats:

### Text Format (Default)

Human-readable format suitable for terminal viewing:

```bash
torrust-tracker-deployer show my-environment
# or explicitly:
torrust-tracker-deployer show my-environment --output-format text
```

### JSON Format

Machine-readable format for automation and scripting:

```bash
torrust-tracker-deployer show my-environment --output-format json
```

#### JSON Output for Provisioned State

```json
{
"name": "my-environment",
"state": "Provisioned",
"provider": "LXD",
"created_at": "2026-02-16T17:56:43.788700279Z",
"infrastructure": {
"instance_ip": "10.140.190.85",
"ssh_port": 22,
"ssh_user": "torrust",
"ssh_key_path": "/home/user/.ssh/torrust_key"
},
"services": null,
"prometheus": null,
"grafana": null,
"state_name": "provisioned"
}
```

#### JSON Output for Running State

```json
{
"name": "my-environment",
"state": "Running",
"provider": "LXD",
"created_at": "2026-02-11T09:52:28.800407753Z",
"infrastructure": {
"instance_ip": "10.140.190.36",
"ssh_port": 22,
"ssh_user": "torrust",
"ssh_key_path": "/home/user/.ssh/torrust_key"
},
"services": {
"udp_trackers": ["udp://udp.tracker.local:6969/announce"],
"https_http_trackers": ["https://http.tracker.local/announce"],
"direct_http_trackers": [],
"localhost_http_trackers": [],
"api_endpoint": "https://api.tracker.local/api",
"api_uses_https": true,
"api_is_localhost_only": false,
"health_check_url": "https://health.tracker.local/health_check",
"health_check_uses_https": true,
"health_check_is_localhost_only": false,
"tls_domains": [
{
"domain": "http.tracker.local",
"internal_port": 7070
},
{
"domain": "api.tracker.local",
"internal_port": 1212
}
]
},
"prometheus": {
"access_note": "Internal only (localhost:9090) - not exposed externally"
},
"grafana": {
"url": "https://grafana.tracker.local/",
"uses_https": true
},
"state_name": "running"
}
```

## Examples

### Basic usage
Expand All @@ -113,6 +207,37 @@ else
fi
```

### Parse JSON output for automation

```bash
#!/bin/bash
# Extract tracker URL from environment
API_URL=$(torrust-tracker-deployer show my-env -o json | \
jq -r '.services.api_endpoint // empty')

if [ -n "$API_URL" ]; then
echo "API available at: $API_URL"
curl "$API_URL/stats"
else
echo "Service not yet running"
fi
```

### Monitor environment state

```bash
#!/bin/bash
# Check if environment is fully running
STATE=$(torrust-tracker-deployer show my-env -o json | \
jq -r '.state_name')

if [ "$STATE" = "running" ]; then
echo "✓ Environment is fully operational"
else
echo "⚠ Environment is in '$STATE' state"
fi
```

### Quick reference for SSH connection

```bash
Expand Down
3 changes: 2 additions & 1 deletion src/application/command_handlers/show/info/grafana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use std::net::IpAddr;

use serde::Serialize;
use url::Url;

use crate::domain::grafana::GrafanaConfig;
Expand All @@ -13,7 +14,7 @@ use crate::domain::grafana::GrafanaConfig;
/// This information shows the status of the Grafana service when configured.
/// Grafana provides dashboards for visualizing tracker metrics.
/// Note: Grafana requires Prometheus to be configured.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct GrafanaInfo {
/// Grafana dashboard URL
pub url: Url,
Expand Down
5 changes: 3 additions & 2 deletions src/application/command_handlers/show/info/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod tracker;
use std::net::IpAddr;

use chrono::{DateTime, Utc};
use serde::Serialize;

pub use self::grafana::GrafanaInfo;
pub use self::prometheus::PrometheusInfo;
Expand All @@ -28,7 +29,7 @@ pub use self::tracker::{LocalhostServiceInfo, ServiceInfo, TlsDomainInfo};
/// This DTO contains all information about an environment that can be
/// displayed to the user. It is state-aware and contains optional fields
/// that are populated based on the environment's current state.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct EnvironmentInfo {
/// Name of the environment
pub name: String,
Expand Down Expand Up @@ -113,7 +114,7 @@ impl EnvironmentInfo {
/// Infrastructure details for an environment
///
/// This information is available after the environment has been provisioned.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct InfrastructureInfo {
/// Instance IP address
pub instance_ip: IpAddr,
Expand Down
4 changes: 3 additions & 1 deletion src/application/command_handlers/show/info/prometheus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
//!
//! This module contains DTOs for the Prometheus service.

use serde::Serialize;

/// Prometheus metrics service information for display purposes
///
/// This information shows the status of the Prometheus service when configured.
/// Prometheus collects and stores metrics from the tracker service.
/// It can be used independently or as a data source for Grafana.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct PrometheusInfo {
/// Description of how to access Prometheus (internal only)
pub access_note: String,
Expand Down
8 changes: 5 additions & 3 deletions src/application/command_handlers/show/info/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use std::net::IpAddr;

use serde::Serialize;

use crate::domain::grafana::GrafanaConfig;
use crate::domain::tracker::config::is_localhost;
use crate::domain::tracker::TrackerConfig;
Expand All @@ -12,7 +14,7 @@ use crate::domain::tracker::TrackerConfig;
///
/// This information is available for Released and Running states and shows
/// the tracker services configured for the environment.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct ServiceInfo {
/// UDP tracker URLs (e.g., `udp://10.0.0.1:6969/announce`)
Expand Down Expand Up @@ -50,7 +52,7 @@ pub struct ServiceInfo {
}

/// Information about a localhost-only service (for SSH tunnel hint)
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct LocalhostServiceInfo {
/// The service name (e.g., `http_tracker_1`)
pub service_name: String,
Expand All @@ -59,7 +61,7 @@ pub struct LocalhostServiceInfo {
}

/// Information about a TLS-enabled domain for /etc/hosts hint
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct TlsDomainInfo {
/// The domain name
pub domain: String,
Expand Down
23 changes: 18 additions & 5 deletions src/presentation/controllers/show/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use crate::application::command_handlers::show::info::EnvironmentInfo;
use crate::application::command_handlers::show::{ShowCommandHandler, ShowCommandHandlerError};
use crate::domain::environment::name::EnvironmentName;
use crate::domain::environment::repository::EnvironmentRepository;
use crate::presentation::views::commands::show::TextView;
use crate::presentation::input::cli::OutputFormat;
use crate::presentation::views::commands::show::{JsonView, TextView};
use crate::presentation::views::progress::ProgressReporter;
use crate::presentation::views::UserOutput;

Expand Down Expand Up @@ -98,19 +99,24 @@ impl ShowCommandController {
/// # Arguments
///
/// * `environment_name` - Name of the environment to show
/// * `output_format` - Output format (Text or Json)
///
/// # Errors
///
/// Returns `ShowSubcommandError` if any step fails
pub fn execute(&mut self, environment_name: &str) -> Result<(), ShowSubcommandError> {
pub fn execute(
&mut self,
environment_name: &str,
output_format: OutputFormat,
) -> Result<(), ShowSubcommandError> {
// Step 1: Validate environment name
let env_name = self.validate_environment_name(environment_name)?;

// Step 2: Load environment via application layer
let env_info = self.load_environment(&env_name)?;

// Step 3: Display information
self.display_information(&env_info)?;
self.display_information(&env_info, output_format)?;

Ok(())
}
Expand Down Expand Up @@ -185,18 +191,25 @@ impl ShowCommandController {
///
/// Following the MVC pattern with functional composition:
/// - Model: `EnvironmentInfo` (application layer DTO)
/// - View: `TextView::render()` (formatting)
/// - View: `TextView::render()` or `JsonView::render()` (formatting)
/// - Controller (this method): Orchestrates the pipeline
/// - Output: `ProgressReporter::result()` (routing to stdout)
fn display_information(
&mut self,
env_info: &EnvironmentInfo,
output_format: OutputFormat,
) -> Result<(), ShowSubcommandError> {
self.progress
.start_step(ShowStep::DisplayInformation.description())?;

// Render using appropriate view based on output format (Strategy Pattern)
let output = match output_format {
OutputFormat::Text => TextView::render(env_info),
OutputFormat::Json => JsonView::render(env_info),
};

// Pipeline: EnvironmentInfo → render → output to stdout
self.progress.result(&TextView::render(env_info))?;
self.progress.result(&output)?;

self.progress.complete_step(Some("Information displayed"))?;

Expand Down
2 changes: 1 addition & 1 deletion src/presentation/dispatch/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ pub async fn route_command(
context
.container()
.create_show_controller()
.execute(&environment)?;
.execute(&environment, context.output_format())?;
Ok(())
}
Commands::List => {
Expand Down
6 changes: 4 additions & 2 deletions src/presentation/views/commands/show/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
//!
//! This module follows the Strategy Pattern for rendering:
//! - `TextView`: Renders human-readable text output with environment details
//! - `JsonView`: Renders machine-readable JSON output for automation
//!
//! # Structure
//!
//! - `views/`: View rendering implementations
//! - `mod.rs`: Main `TextView` with composition of helper views
//! - `text_view.rs`: Main `TextView` with composition of helper views
//! - `json_view.rs`: Main `JsonView` for JSON serialization
//! - Helper views: basic, infrastructure, `tracker_services`, prometheus, grafana, `https_hint`, `next_step`

pub mod views;

// Re-export main types for convenience
pub use views::TextView;
pub use views::{JsonView, TextView};
Loading
Loading