blob: acf623c71d134f03a555e8c1bd9e0cad35c68f23 [file] [log] [blame]
// Copyright 2019 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 contains flag definitions for the config_parser package as well
// as all the functions which make direct use of these flags.
// TODO(fxbug.dev/87114): Refactor and test. This is half of the logic that was ripped out
// config_parser_main.go and it still needs to be refactored.
package config_parser
import (
"flag"
"fmt"
"time"
)
var (
repoUrls flagList
configDirs flagList
configFile = flag.String("config_file", "", "File containing the config for a single project. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
customerId = flag.Int64("customer_id", -1, "Customer Id for the config to be read. Must be set if and only if 'config_file' is set.")
projectId = flag.Int64("project_id", -1, "Project Id for the config to be read. Must be set if and only if 'config_file' is set.")
gitTimeoutSec = flag.Int64("git_timeout", 60, "How many seconds should I wait on git commands?")
)
type flagList []string
func (f *flagList) String() string {
return fmt.Sprintf("%q", *f)
}
func (f *flagList) Set(value string) error {
*f = append(*f, value)
return nil
}
// Initialization function that is run before any main().
func init() {
flag.Var(&repoUrls, "repo_url", "URL of the repository containing the config, can be specified multiple times. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
flag.Var(&configDirs, "config_dir", "Directory containing the config, can be specified multiple times. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
}
// checkFlags verifies that the specified flags are compatible with each other.
func checkFlags() error {
if (len(repoUrls) == 0) == (len(configDirs) == 0) == (*configFile == "") {
return fmt.Errorf("Exactly one of 'repo_url', 'config_file' and 'config_dir' must be set.")
}
if *configFile == "" && len(configDirs) == 0 && (*customerId >= 0 || *projectId >= 0) {
return fmt.Errorf("'customer_id' and 'project_id' must be set if and only if 'config_file' or 'config_dir' are set.")
}
if *configFile != "" && (*customerId < 0 || *projectId < 0) {
return fmt.Errorf("If 'config_file' is set, both 'customer_id' and 'project_id' must be set.")
}
return nil
}
// appendNewCustomers adds the new project configs to the list, verifying that
// none of their customers overlap with existing customers.
func appendNewCustomers(existingCustomers map[uint32]string, configs []ProjectConfigData, newConfigs ...ProjectConfigData) (map[uint32]string, []ProjectConfigData, error) {
for _, config := range newConfigs {
if customerName, ok := existingCustomers[config.CustomerId]; ok {
return nil, nil, fmt.Errorf("Duplicate customer ID %v for customers %s and %s", config.CustomerId, customerName, config.CustomerName)
}
}
configs = append(configs, newConfigs...)
for _, config := range newConfigs {
existingCustomers[config.CustomerId] = config.CustomerName
}
return existingCustomers, configs, nil
}
// ParseProjectConfigDataFromFlags uses the specified flags to find the
// specified registry, read and parse it.
func ParseProjectConfigDataFromFlags() ([]ProjectConfigData, error) {
if err := checkFlags(); err != nil {
return nil, err
}
existingCustomers := make(map[uint32]string)
configs := []ProjectConfigData{}
var pc ProjectConfigData
var err error
if len(repoUrls) != 0 {
gitTimeout := time.Duration(*gitTimeoutSec) * time.Second
var repoConfigs []ProjectConfigData
for _, repoUrl := range repoUrls {
if repoConfigs, err = ReadConfigFromRepo(repoUrl, gitTimeout); err != nil {
return nil, err
}
if existingCustomers, configs, err = appendNewCustomers(existingCustomers, configs, repoConfigs...); err != nil {
return nil, err
}
}
} else if *configFile != "" {
version := CobaltVersion1
if pc, err = ReadConfigFromYaml(*configFile, uint32(*customerId), uint32(*projectId), CobaltVersion(version)); err != nil {
return nil, err
}
if existingCustomers, configs, err = appendNewCustomers(existingCustomers, configs, pc); err != nil {
return nil, err
}
} else if *customerId >= 0 && *projectId >= 0 {
for _, configDir := range configDirs {
if pc, err = ReadProjectConfigDataFromDir(configDir, uint32(*customerId), uint32(*projectId)); err != nil {
return nil, err
}
if existingCustomers, configs, err = appendNewCustomers(existingCustomers, configs, pc); err != nil {
return nil, err
}
}
} else {
var dirConfigs []ProjectConfigData
for _, configDir := range configDirs {
if dirConfigs, err = ReadConfigFromDir(configDir); err != nil {
return nil, err
}
if existingCustomers, configs, err = appendNewCustomers(existingCustomers, configs, dirConfigs...); err != nil {
return nil, err
}
}
}
return configs, err
}
// GetConfigFilesListFromFlags returns a list of all the files that comprise
// the registry being parsed. This can be used to generate a depfile.
func GetConfigFilesListFromFlags() ([]string, error) {
if err := checkFlags(); err != nil {
return nil, err
}
if *configFile != "" {
return []string{*configFile}, nil
} else if len(configDirs) != 0 {
files := []string{}
var dirFiles []string
var err error
for _, configDir := range configDirs {
dirFiles, err = GetConfigFilesListFromConfigDir(configDir)
files = append(files, dirFiles...)
}
return files, err
}
return nil, fmt.Errorf("-dep_file requires -config_dir or -config_file")
}