Skip to content

Commit

Permalink
feat(components): add additional config for graph output command (#21194
Browse files Browse the repository at this point in the history
)

* feat(components): add additional config for graph output command

This adds additional `graph` config to all components which enables adding any `node_attributes` to
the generated graph.

Closes: #21043

* Add changelog entry

* Add command note to the changelog

Co-authored-by: Jesse Szwedko <[email protected]>

* Fix `dot_graph` docs capitalization

Co-authored-by: Jesse Szwedko <[email protected]>

* Fix clippy warnings

* Fix clippy warnings

* Add example for graph config

* Update changelog.d/21194_dot_graph_attributes.feature.md

Co-authored-by: Sandra (neko) <[email protected]>

* Update proxy config docs

---------

Co-authored-by: Jesse Szwedko <[email protected]>
Co-authored-by: Sandra (neko) <[email protected]>
  • Loading branch information
3 people authored Sep 16, 2024
1 parent e71016c commit f5b9265
Show file tree
Hide file tree
Showing 12 changed files with 240 additions and 67 deletions.
3 changes: 3 additions & 0 deletions changelog.d/21194_dot_graph_attributes.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Adds support for additional graph configuration on each component so that users can add arbitrary graphviz node attributes when generating a graph via `vector graph`.

authors: esensar
2 changes: 1 addition & 1 deletion lib/vector-core/src/config/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl NoProxyInterceptor {
/// Configure to proxy traffic through an HTTP(S) proxy when making external requests.
///
/// Similar to common proxy configuration convention, you can set different proxies
/// to use based on the type of traffic being proxied, as well as set specific hosts that
/// to use based on the type of traffic being proxied. You can also set specific hosts that
/// should not be proxied.
#[configurable_component]
#[configurable(metadata(docs::advanced))]
Expand Down
29 changes: 29 additions & 0 deletions src/config/dot_graph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::collections::HashMap;

use vector_lib::configurable::configurable_component;

/// Extra graph configuration
///
/// Configure output for component when generated with graph command
#[configurable_component]
#[configurable(metadata(docs::advanced))]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct GraphConfig {
/// Node attributes to add to this component's node in resulting graph
///
/// They are added to the node as provided
#[configurable(metadata(
docs::additional_props_description = "A single graph node attribute in graphviz DOT language.",
docs::examples = "example_graph_options()"
))]
pub node_attributes: HashMap<String, String>,
}

fn example_graph_options() -> HashMap<String, String> {
HashMap::<_, _>::from_iter([
("name".to_string(), "Example Node".to_string()),
("color".to_string(), "red".to_string()),
("width".to_string(), "5.0".to_string()),
])
}
1 change: 1 addition & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod builder;
mod cmd;
mod compiler;
mod diff;
mod dot_graph;
mod enrichment_table;
pub mod format;
mod graph;
Expand Down
8 changes: 7 additions & 1 deletion src/config/sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use vector_lib::{
sink::VectorSink,
};

use super::{schema, ComponentKey, ProxyConfig, Resource};
use super::{dot_graph::GraphConfig, schema, ComponentKey, ProxyConfig, Resource};
use crate::extra_context::ExtraContext;
use crate::sinks::{util::UriSerde, Healthcheck};

Expand Down Expand Up @@ -53,6 +53,10 @@ pub struct SinkOuter<T>
where
T: Configurable + Serialize + 'static,
{
#[configurable(derived)]
#[serde(default, skip_serializing_if = "vector_lib::serde::is_default")]
pub graph: GraphConfig,

#[configurable(derived)]
pub inputs: Inputs<T>,

Expand Down Expand Up @@ -96,6 +100,7 @@ where
healthcheck_uri: None,
inner: inner.into(),
proxy: Default::default(),
graph: Default::default(),
}
}

Expand Down Expand Up @@ -152,6 +157,7 @@ where
healthcheck: self.healthcheck,
healthcheck_uri: self.healthcheck_uri,
proxy: self.proxy,
graph: self.graph,
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/config/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use vector_lib::{
source::Source,
};

use super::{schema, ComponentKey, ProxyConfig, Resource};
use super::{dot_graph::GraphConfig, schema, ComponentKey, ProxyConfig, Resource};
use crate::{extra_context::ExtraContext, shutdown::ShutdownSignal, SourceSender};

pub type BoxedSource = Box<dyn SourceConfig>;
Expand Down Expand Up @@ -54,6 +54,10 @@ pub struct SourceOuter {
#[serde(default, skip_serializing_if = "vector_lib::serde::is_default")]
pub proxy: ProxyConfig,

#[configurable(derived)]
#[serde(default, skip_serializing_if = "vector_lib::serde::is_default")]
pub graph: GraphConfig,

#[serde(default, skip)]
pub sink_acknowledgements: bool,

Expand All @@ -66,6 +70,7 @@ impl SourceOuter {
pub(crate) fn new<I: Into<BoxedSource>>(inner: I) -> Self {
Self {
proxy: Default::default(),
graph: Default::default(),
sink_acknowledgements: false,
inner: inner.into(),
}
Expand Down
12 changes: 11 additions & 1 deletion src/config/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use vector_lib::{
transform::Transform,
};

use super::dot_graph::GraphConfig;
use super::schema::Options as SchemaOptions;
use super::ComponentKey;
use super::OutputId;
Expand Down Expand Up @@ -56,6 +57,10 @@ pub struct TransformOuter<T>
where
T: Configurable + Serialize + 'static,
{
#[configurable(derived)]
#[serde(default, skip_serializing_if = "vector_lib::serde::is_default")]
pub graph: GraphConfig,

#[configurable(derived)]
pub inputs: Inputs<T>,

Expand All @@ -75,7 +80,11 @@ where
{
let inputs = Inputs::from_iter(inputs);
let inner = inner.into();
TransformOuter { inputs, inner }
TransformOuter {
inputs,
inner,
graph: Default::default(),
}
}

pub(super) fn map_inputs<U>(self, f: impl Fn(&T) -> U) -> TransformOuter<U>
Expand All @@ -94,6 +103,7 @@ where
TransformOuter {
inputs: Inputs::from_iter(inputs),
inner: self.inner,
graph: self.graph,
}
}
}
Expand Down
39 changes: 35 additions & 4 deletions src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::collections::HashMap;
use std::fmt::Write as _;
use std::path::PathBuf;

use clap::Parser;
use itertools::Itertools;

use crate::config;

Expand Down Expand Up @@ -65,6 +67,17 @@ impl Opts {
}
}

fn node_attributes_to_string(attributes: &HashMap<String, String>, default_shape: &str) -> String {
let mut attrs = attributes.clone();
if !attrs.contains_key("shape") {
attrs.insert("shape".to_string(), default_shape.to_string());
}
return attrs
.iter()
.map(|(k, v)| format!("{}=\"{}\"", k, v))
.join(" ");
}

pub(crate) fn cmd(opts: &Opts) -> exitcode::ExitCode {
let paths = opts.paths_with_formats();
let paths = match config::process_paths(&paths) {
Expand All @@ -85,12 +98,24 @@ pub(crate) fn cmd(opts: &Opts) -> exitcode::ExitCode {

let mut dot = String::from("digraph {\n");

for (id, _source) in config.sources() {
writeln!(dot, " \"{}\" [shape=trapezium]", id).expect("write to String never fails");
for (id, source) in config.sources() {
writeln!(
dot,
" \"{}\" [{}]",
id,
node_attributes_to_string(&source.graph.node_attributes, "trapezium")
)
.expect("write to String never fails");
}

for (id, transform) in config.transforms() {
writeln!(dot, " \"{}\" [shape=diamond]", id).expect("write to String never fails");
writeln!(
dot,
" \"{}\" [{}]",
id,
node_attributes_to_string(&transform.graph.node_attributes, "diamond")
)
.expect("write to String never fails");

for input in transform.inputs.iter() {
if let Some(port) = &input.port {
Expand All @@ -108,7 +133,13 @@ pub(crate) fn cmd(opts: &Opts) -> exitcode::ExitCode {
}

for (id, sink) in config.sinks() {
writeln!(dot, " \"{}\" [shape=invtrapezium]", id).expect("write to String never fails");
writeln!(
dot,
" \"{}\" [{}]",
id,
node_attributes_to_string(&sink.graph.node_attributes, "invtrapezium")
)
.expect("write to String never fails");

for input in &sink.inputs {
if let Some(port) = &input.port {
Expand Down
30 changes: 29 additions & 1 deletion website/cue/reference/components/base/sinks.cue
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,34 @@ base: components: sinks: configuration: {
}
}
}
graph: {
description: """
Extra graph configuration
Configure output for component when generated with graph command
"""
required: false
type: object: options: node_attributes: {
description: """
Node attributes to add to this component's node in resulting graph
They are added to the node as provided
"""
required: false
type: object: {
examples: [{
color: "red"
name: "Example Node"
width: "5.0"
}]
options: "*": {
description: "A single graph node attribute in graphviz DOT language."
required: true
type: string: {}
}
}
}
}
healthcheck: {
description: "Healthcheck configuration."
required: false
Expand Down Expand Up @@ -119,7 +147,7 @@ base: components: sinks: configuration: {
Configure to proxy traffic through an HTTP(S) proxy when making external requests.
Similar to common proxy configuration convention, you can set different proxies
to use based on the type of traffic being proxied, as well as set specific hosts that
to use based on the type of traffic being proxied. You can also set specific hosts that
should not be proxied.
"""
required: false
Expand Down
Loading

0 comments on commit f5b9265

Please sign in to comment.