risedev/task/
frontend_service.rs

1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::process::Command;
16
17use anyhow::{Result, anyhow};
18use itertools::Itertools;
19
20use super::{ExecuteContext, Task};
21use crate::util::{get_program_args, get_program_env_cmd, get_program_name};
22use crate::{FrontendConfig, add_tempo_endpoint};
23
24pub struct FrontendService {
25    config: FrontendConfig,
26}
27
28impl FrontendService {
29    pub fn new(config: FrontendConfig) -> Result<Self> {
30        Ok(Self { config })
31    }
32
33    /// Apply command args according to config
34    pub fn apply_command_args(cmd: &mut Command, config: &FrontendConfig) -> Result<()> {
35        cmd.arg("--listen-addr")
36            .arg(format!("{}:{}", config.listen_address, config.port))
37            .arg("--advertise-addr")
38            .arg(format!("{}:{}", config.address, config.port))
39            .arg("--prometheus-listener-addr")
40            .arg(format!(
41                "{}:{}",
42                config.listen_address, config.exporter_port
43            ))
44            .arg("--health-check-listener-addr")
45            .arg(format!(
46                "{}:{}",
47                config.listen_address, config.health_check_port
48            ));
49
50        let provide_meta_node = config.provide_meta_node.as_ref().unwrap();
51        if provide_meta_node.is_empty() {
52            return Err(anyhow!(
53                "Cannot configure node: no meta node found in this configuration."
54            ));
55        } else {
56            cmd.arg("--meta-addr").arg(
57                provide_meta_node
58                    .iter()
59                    .map(|meta_node| format!("http://{}:{}", meta_node.address, meta_node.port))
60                    .join(","),
61            );
62        }
63
64        let provide_tempo = config.provide_tempo.as_ref().unwrap();
65        add_tempo_endpoint(provide_tempo, cmd)?;
66
67        // Add Prometheus endpoint and selector if configured
68        match config.provide_prometheus.as_ref().unwrap().as_slice() {
69            [] => {}
70            [prometheus] => {
71                cmd.arg("--prometheus-endpoint")
72                    .arg(format!("http://{}:{}", prometheus.address, prometheus.port));
73                // Note: prometheus_selector is not currently configured in risedev.yml
74                // but we can add it here if needed in the future
75            }
76            _ => {
77                return Err(anyhow!(
78                    "unexpected prometheus config {:?}, only 1 instance is supported",
79                    config.provide_prometheus
80                ));
81            }
82        }
83
84        Ok(())
85    }
86}
87
88impl Task for FrontendService {
89    fn execute(&mut self, ctx: &mut ExecuteContext<impl std::io::Write>) -> anyhow::Result<()> {
90        ctx.service(self);
91        ctx.pb.set_message("starting...");
92
93        let mut cmd = ctx.risingwave_cmd("frontend-node")?;
94
95        Self::apply_command_args(&mut cmd, &self.config)?;
96
97        if !self.config.user_managed {
98            ctx.run_command(ctx.tmux_run(cmd)?)?;
99            ctx.pb.set_message("started");
100        } else {
101            ctx.pb.set_message("user managed");
102            writeln!(
103                &mut ctx.log,
104                "Please use the following parameters to start the frontend:\n{}\n{} {}\n\n",
105                get_program_env_cmd(&cmd),
106                get_program_name(&cmd),
107                get_program_args(&cmd)
108            )?;
109        }
110
111        Ok(())
112    }
113
114    fn id(&self) -> String {
115        self.config.id.clone()
116    }
117}