blob: 439b66ee0c5e9741fc45b68f1afaff1dd4e14957 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements reading the Cobalt configuration from a directory.
// See ReadConfigFromDir for details.
package config_parser
import (
"config"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
// ReadConfigFromDir reads the whole configuration for Cobalt from a directory on the file system.
// It is assumed that <rootDir>/projects.yaml contains the customers and projects list. (see project_list.go)
// It is assumed that <rootDir>/<customerName>/<projectName>/config.yaml
// contains the configuration for a project. (see project_config.go)
func ReadConfigFromDir(rootDir string) (c config.CobaltConfig, err error) {
r, err := newConfigReaderForDir(rootDir)
if err != nil {
return c, err
}
l := []projectConfig{}
if err := readConfig(r, &l); err != nil {
return c, err
}
return mergeConfigs(l), nil
}
// ReadConfigFromYaml reads the configuration for a single project from a single yaml file.
// See project_config.go for the format.
func ReadConfigFromYaml(yamlConfigPath string, customerId uint32, projectId uint32) (c config.CobaltConfig, err error) {
yamlConfig, err := ioutil.ReadFile(yamlConfigPath)
if err != nil {
return c, err
}
p := projectConfig{}
p.customerId = customerId
p.projectId = projectId
if err := parseProjectConfig(string(yamlConfig), &p); err != nil {
return c, err
}
c.EncodingConfigs = p.projectConfig.EncodingConfigs
c.MetricConfigs = p.projectConfig.MetricConfigs
c.ReportConfigs = p.projectConfig.ReportConfigs
return c, nil
}
// GetConfigFilesListFromConfigDir reads the configuration for Cobalt from a
// directory on the file system (See ReadConfigFromDir) and returns the list
// of files which constitute the configuration. The purpose is generating a
// list of dependencies.
func GetConfigFilesListFromConfigDir(rootDir string) (files []string, err error) {
r, err := newConfigDirReader(rootDir)
if err != nil {
return files, err
}
l := []projectConfig{}
if err := readProjectsList(r, &l); err != nil {
return files, err
}
files = append(files, r.customersFilePath())
for i, _ := range l {
c := &(l[i])
files = append(files, r.projectFilePath(c.customerName, c.projectName))
}
return files, nil
}
// configReader is an interface that returns configuration data in the yaml format.
type configReader interface {
// Returns the yaml representation of the customer and project list.
// See project_list.go
Customers() (string, error)
// Returns the yaml representation of the configuration for a particular project.
// See project_config.go
Project(customerName string, projectName string) (string, error)
}
// configDirReader is an implementation of configReader where the configuration
// data is stored in configDir.
type configDirReader struct {
configDir string
}
// newConfigDirReader returns a pointer to a configReader which will read the
// Cobalt configuration stored in the provided directory.
func newConfigDirReader(configDir string) (r *configDirReader, err error) {
info, err := os.Stat(configDir)
if err != nil {
return nil, err
}
if !info.IsDir() {
return nil, fmt.Errorf("%v is not a directory.", configDir)
}
return &configDirReader{configDir: configDir}, nil
}
// newConfigReaderForDir returns a configReader which will read the Cobalt
// configuration stored in the provided directory.
func newConfigReaderForDir(configDir string) (r configReader, err error) {
return newConfigDirReader(configDir)
}
func (r *configDirReader) customersFilePath() string {
// The customer and project list is at <rootDir>/projects.yaml
return filepath.Join(r.configDir, "projects.yaml")
}
func (r *configDirReader) Customers() (string, error) {
customerList, err := ioutil.ReadFile(r.customersFilePath())
if err != nil {
return "", err
}
return string(customerList), nil
}
func (r *configDirReader) projectFilePath(customerName string, projectName string) string {
// A project's config is at <rootDir>/<customerName>/<projectName>/config.yaml
return filepath.Join(r.configDir, customerName, projectName, "config.yaml")
}
func (r *configDirReader) Project(customerName string, projectName string) (string, error) {
projectConfig, err := ioutil.ReadFile(r.projectFilePath(customerName, projectName))
if err != nil {
return "", err
}
return string(projectConfig), nil
}
func readProjectsList(r configReader, l *[]projectConfig) (err error) {
// First, we get and parse the customer list.
customerListYaml, err := r.Customers()
if err != nil {
return err
}
if err = parseCustomerList(customerListYaml, l); err != nil {
return err
}
return nil
}
// readConfig reads and parses the configuration for all projects from a configReader.
func readConfig(r configReader, l *[]projectConfig) (err error) {
if err = readProjectsList(r, l); err != nil {
return err
}
// Then, based on the customer list, we read and parse all the project configs.
for i, _ := range *l {
c := &((*l)[i])
if err = readProjectConfig(r, c); err != nil {
return fmt.Errorf("Error reading config for %v %v: %v", c.customerName, c.projectName, err)
}
}
return nil
}
// readProjectConfig reads the configuration of a particular project.
func readProjectConfig(r configReader, c *projectConfig) (err error) {
configYaml, err := r.Project(c.customerName, c.projectName)
if err != nil {
return err
}
return parseProjectConfig(configYaml, c)
}
// mergeConfigs accepts a list of projectConfigs each of which contains the
// encoding, metric and report configs for a particular project and aggregates
// all those into a single CobaltConfig proto.
func mergeConfigs(l []projectConfig) (s config.CobaltConfig) {
for _, c := range l {
s.EncodingConfigs = append(s.EncodingConfigs, c.projectConfig.EncodingConfigs...)
s.MetricConfigs = append(s.MetricConfigs, c.projectConfig.MetricConfigs...)
s.ReportConfigs = append(s.ReportConfigs, c.projectConfig.ReportConfigs...)
}
return s
}