rusty_ci/buildbot/
masterconfig.rs

1use crate::{
2    unmatched_quotes, unwrap, Builder, MailNotifier, MergeRequestHandler, Scheduler, Worker,
3};
4
5use rusty_yaml::Yaml;
6use std::fmt::{Display, Error, Formatter};
7use std::process::exit;
8
9/// This struct represents the configuration file for the master.
10/// This file contains the Python code for the builders and the schedulers.
11/// In addition, it contains some basic data such as the title for the web ui,
12/// the title url for the webui, and so on.
13///
14/// For more information on how the master configuration file works,
15/// see the documentation for buildbot on their website: https://buildbot.net/
16pub struct MasterConfig {
17    title: String,
18    title_url: String,
19    git_repo: String,
20    webserver_ip: String,
21    webserver_port: String,
22    poll_interval: String,
23    mail_notifier: Option<MailNotifier>,
24    merge_request_handler: MergeRequestHandler,
25    builders: Vec<Builder>,
26    schedulers: Vec<Scheduler>,
27    workers: Vec<Worker>,
28}
29
30/// This is impl the for MasterConfig struct.
31impl MasterConfig {
32    pub fn set_mail_notifier(&mut self, mail_notifier: MailNotifier) {
33        self.mail_notifier = Some(mail_notifier);
34    }
35
36    pub fn get_workers(&self) -> Vec<Worker> {
37        self.workers.clone()
38    }
39}
40
41/// This impl converts a Yaml file into a MasterConfig object.
42/// This is intended to take the entire input yaml file.
43impl From<Yaml> for MasterConfig {
44    fn from(yaml: Yaml) -> Self {
45        // Verify that the yaml file doesnt have unmatched quotes!
46        if let Some(line) = unmatched_quotes(&yaml) {
47            error!("There was a problem creating the master configuration file: unmatched quotes in the line '{}'", line.trim());
48            exit(1);
49        }
50
51        // Verify that the yaml section contains all the necessary subsections
52        for section in [
53            "master",
54            "workers",
55            "builders",
56            "schedulers",
57            "merge-request-handler",
58        ]
59        .iter()
60        {
61            if !yaml.has_section(section) {
62                error!("There was an error creating the master configuration file: '{}' section was not declared", section);
63                exit(1);
64            }
65        }
66
67        // Get the master susbsection, the subsection holding the web gui and git information
68        let master = yaml.get_section("master").unwrap();
69
70        // Verify the master subsection contains all the proper data
71        for section in [
72            "title",
73            "title-url",
74            "repo",
75            "webserver-ip",
76            "webserver-port",
77            "poll-interval",
78        ]
79        .iter()
80        {
81            if !master.has_section(section) {
82                error!("There was an error creating the master configuration file: The '{}' section is not specified for master", section);
83                exit(1);
84            }
85        }
86
87        let merge_request_handler =
88            MergeRequestHandler::from(yaml.get_section("merge-request-handler").unwrap());
89
90        // Get schedulers, builders, and workers from the yaml file.
91        // Because we previously verified that each subsection exists,
92        // we can unwrap the result without a problem.
93        let mut schedulers = vec![];
94        for scheduler in yaml.get_section("schedulers").unwrap() {
95            schedulers.push(Scheduler::from(scheduler));
96        }
97
98        // Because we previously verified that each subsection exists,
99        // we can unwrap the result without a problem.
100        let mut builders = vec![];
101        for builder in yaml.get_section("builders").unwrap() {
102            builders.push(Builder::from(builder));
103        }
104
105        // Because we previously verified that each subsection exists,
106        // we can unwrap the result without a problem.
107        let mut workers = vec![];
108        for worker in yaml.get_section("workers").unwrap() {
109            workers.push(Worker::from(worker));
110        }
111
112        // Get all the data from the master subsection
113        let title = unwrap(&master, "title");
114        let title_url = unwrap(&master, "title-url");
115        let git_repo = unwrap(&master, "repo");
116        let webserver_ip = unwrap(&master, "webserver-ip");
117        let webserver_port = unwrap(&master, "webserver-port");
118        let poll_interval = unwrap(&master, "poll-interval");
119
120        // Return the whole master configuration file
121
122        Self {
123            title,
124            title_url,
125            git_repo,
126            webserver_ip,
127            webserver_port,
128            poll_interval,
129            mail_notifier: None,
130            merge_request_handler,
131            builders,
132            schedulers,
133            workers,
134        }
135    }
136}
137
138/// Converts a MasterConfig instance into the Python master configuration file for buildbot
139impl Display for MasterConfig {
140    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
141        write!(
142            f,
143            r#"
144# -*- python -*-
145# ex: set filetype=python:
146import re
147import json
148import requests as req
149from dateutil.parser import parse as dateparse
150from buildbot.plugins import *
151from buildbot.www.hooks.github import GitHubEventHandler
152
153# This is a sample buildmaster config file. It must be installed as
154# 'master.cfg' in your buildmaster's base directory.
155
156# This is the dictionary that the buildmaster pays attention to. We also use
157# a shorter alias to save typing.
158c = BuildmasterConfig = {{}}
159
160####### WORKERS
161
162# The 'workers' list defines the set of recognized workers. Each element is
163# a Worker object, specifying a unique worker name and password.  The same
164# worker name and password must be configured on the worker.
165c['workers'] = [{worker_info}]
166c['protocols'] = {{'pb': {{'port': 9989}}}}
167
168
169c['www'] = dict(port={webserver_port},
170                plugins=dict(waterfall_view={{}}, console_view={{}}, grid_view={{}}))
171
172c['change_source'] = []
173c['services'] = []
174
175{mail_notifier}
176
177{merge_request_handler}
178
179c['change_source'].append(changes.GitPoller(
180        "{git_repo}",
181        workdir='gitpoller-workdir', branches=True, # poll all branches
182        pollInterval={poll_interval}))
183
184c['schedulers'] = []
185c['builders'] = []
186
187{schedulers}
188{builders}
189
190
191c['title'] = "{title}"
192c['titleURL'] = "{title_url}"
193
194c['buildbotURL'] = "http://{webserver_ip}:{webserver_port}/"
195
196c['db'] = {{
197    # This specifies what database buildbot uses to store its state.  You can leave
198    # this at its default for all but the largest installations.
199    'db_url' : "sqlite:///state.sqlite",
200}}
201        "#,
202            title = self.title,
203            title_url = self.title_url,
204            webserver_ip = self.webserver_ip,
205            webserver_port = self.webserver_port,
206            git_repo = self.git_repo,
207            merge_request_handler = self.merge_request_handler,
208            mail_notifier = match &self.mail_notifier {
209                Some(mn) => mn.to_string(),
210                None => "".to_string(),
211            },
212            worker_info = self
213                .workers
214                .iter()
215                .map(|w| {
216                    format!(
217                        "worker.Worker(\"{}\", \"{}\")",
218                        w.get_name(),
219                        w.get_password()
220                    )
221                })
222                .collect::<Vec<String>>()
223                .join(", "),
224            poll_interval = self.poll_interval,
225            schedulers = self
226                .schedulers
227                .iter()
228                .map(|s| s.to_string())
229                .collect::<Vec<String>>()
230                .join("\n\n"),
231            builders = self
232                .builders
233                .iter()
234                .map(|b| b.to_string())
235                .collect::<Vec<String>>()
236                .join("\n\n"),
237        )
238    }
239}