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
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions cli/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ homepage.workspace = true
repository.workspace = true

[dependencies]
clap.workspace = true
eyre.workspace = true
indexmap.workspace = true
openstack-cli-core.workspace = true
serde.workspace = true
serde_json.workspace = true
structable = { workspace = true }
thiserror.workspace = true
tracing.workspace = true
yaml_serde.workspace = true
yamlpatch.workspace = true
yamlpath.workspace = true
Expand Down
40 changes: 36 additions & 4 deletions cli/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,41 @@
// SPDX-License-Identifier: Apache-2.0
//! Local client configuration file operations.
//!
//! This crate is the foundation for `osc config` commands that read and
//! edit `clouds.yaml`/`secure.yaml` in place. It currently provides
//! [`yaml_edit`], a comment- and anchor-preserving YAML editor; command
//! implementations built on top of it (e.g. `clouds add`) land separately.
//! This crate is the foundation for `osc config` commands. It currently
//! provides:
//!
//! * [`show`], displaying the effective local CLI configuration
//! (`$XDG_CONFIG_HOME/osc/config.yaml`).
//! * [`yaml_edit`], a comment- and anchor-preserving YAML editor for
//! `clouds.yaml`/`secure.yaml`; command implementations built on top of
//! it (e.g. `clouds add`) land separately.

use clap::{Parser, Subcommand};

use openstack_cli_core::{cli::CliArgs, error::OpenStackCliError};

pub mod show;
pub mod yaml_edit;

/// Local `osc` client configuration.
#[derive(Debug, Parser)]
pub struct ConfigCommand {
/// Config management commands
#[command(subcommand)]
pub command: ConfigCommands,
}

#[allow(missing_docs)]
#[derive(Debug, Subcommand)]
pub enum ConfigCommands {
Show(show::ShowCommand),
}

impl ConfigCommand {
/// Perform command action.
pub async fn take_action<C: CliArgs>(&self, parsed_args: &C) -> Result<(), OpenStackCliError> {
match &self.command {
ConfigCommands::Show(cmd) => cmd.take_action(parsed_args).await,
}
}
}
84 changes: 84 additions & 0 deletions cli/config/src/show.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

//! Show the effective local `osc` CLI configuration.

use clap::Parser;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::info;

use openstack_cli_core::config::Config;
use openstack_cli_core::output::{OutputFor, OutputProcessor};
use openstack_cli_core::{cli::CliArgs, error::OpenStackCliError};
use structable::{StructTable, StructTableOptions};

/// Show the effective local CLI configuration.
///
/// This is the `$XDG_CONFIG_HOME/osc/config.yaml` configuration merged with
/// the built-in defaults. It controls CLI-only behavior (output views,
/// hints) and is unrelated to `clouds.yaml`/`secure.yaml` cloud connection
/// credentials.
#[derive(Debug, Parser)]
pub struct ShowCommand {}

/// A displayable view of the effective CLI configuration.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize, StructTable)]
pub struct ConfigView {
/// Configured output views, keyed by resource.
#[structable(serialize)]
pub views: Value,
/// Configured per-resource command hints.
#[structable(serialize)]
pub command_hints: Value,
/// General hints shown independent of the command.
#[structable(serialize)]
pub hints: Value,
/// Whether hints are shown after a successful command.
#[structable()]
pub enable_hints: bool,
}

impl TryFrom<&Config> for ConfigView {
type Error = eyre::Report;
fn try_from(value: &Config) -> Result<Self, Self::Error> {
Ok(Self {
views: serde_json::to_value(&value.views)?,
command_hints: serde_json::to_value(&value.command_hints)?,
hints: serde_json::to_value(&value.hints)?,
enable_hints: value.enable_hints,
})
}
}

impl ShowCommand {
/// Perform command action.
pub async fn take_action<C: CliArgs>(&self, parsed_args: &C) -> Result<(), OpenStackCliError> {
info!("Show effective CLI configuration");

let op = OutputProcessor::from_args(parsed_args, Some("config"), Some("show"));
let config = parsed_args.config();

match op.target {
OutputFor::Human => {
op.output_human(&ConfigView::try_from(config)?)?;
}
_ => {
op.output_machine(serde_json::to_value(config)?)?;
}
}
op.show_command_hint()?;
Ok(())
}
}
8 changes: 4 additions & 4 deletions cli/core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
//! ```

use eyre::Result;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt,
Expand Down Expand Up @@ -107,7 +107,7 @@ pub enum ConfigBuilderError {
/// Output configuration
///
/// This structure is controlling how the table table is being built for a structure.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ViewConfig {
/// Limit fields (their titles) to be returned
#[serde(default)]
Expand All @@ -121,7 +121,7 @@ pub struct ViewConfig {
}

/// Field output configuration
#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialOrd, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialOrd, PartialEq, Serialize)]
pub struct FieldConfig {
/// Attribute name
pub name: String,
Expand Down Expand Up @@ -150,7 +150,7 @@ const fn _default_true() -> bool {
}

/// OpenStackClient configuration
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
/// Map of views with the key being the resource key `<SERVICE_TYPE>.<RESOURCE>[/<SUBRESOURCE>]`)
/// and the value being an `[OutputConfig]`
Expand Down
1 change: 1 addition & 0 deletions openstack_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ openstack-cli-auth.workspace = true
openstack-cli-block-storage.workspace = true
openstack-cli-catalog.workspace = true
openstack-cli-compute.workspace = true
openstack-cli-config.workspace = true
openstack-cli-container-infrastructure-management.workspace = true
openstack-cli-core.workspace = true
openstack-cli-dns.workspace = true
Expand Down
5 changes: 5 additions & 0 deletions openstack_cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ pub enum TopLevelCommands {
BlockStorage(openstack_cli_block_storage::BlockStorageCommand),
Catalog(openstack_cli_catalog::CatalogCommand),
Compute(openstack_cli_compute::ComputeCommand),
Config(openstack_cli_config::ConfigCommand),
#[command(aliases = ["container-infrastructure-management", "container"])]
ContainerInfrastructure(
openstack_cli_container_infrastructure_management::ContainerInfrastructureCommand,
Expand Down Expand Up @@ -152,6 +153,10 @@ impl Cli {
TopLevelCommands::BlockStorage(args) => args.take_action(self, client).await,
TopLevelCommands::Catalog(args) => args.take_action(self, client).await,
TopLevelCommands::Compute(args) => args.take_action(self, client).await,
// `osc config` never needs a cloud connection and is dispatched
// before this point in `entry_point`; kept here only to make
// this match exhaustive.
TopLevelCommands::Config(_) => unimplemented!(),
TopLevelCommands::ContainerInfrastructure(args) => args.take_action(self, client).await,
TopLevelCommands::Dns(args) => args.take_action(self, client).await,
TopLevelCommands::Identity(args) => args.take_action(self, client).await,
Expand Down
6 changes: 6 additions & 0 deletions openstack_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ pub async fn entry_point() -> Result<(), OpenStackCliError> {
return args.take_action(&cli).await;
}

if let TopLevelCommands::Config(args) = &cli.command {
// `osc config` commands edit/read local configuration files and
// must not trigger cloud authentication.
return args.take_action(&cli).await;
}

if let TopLevelCommands::Auth(args) = &cli.command
&& let Some(cmd) = args.as_offline_cache_clear_all()
{
Expand Down
Loading