blob: 94fd7961a25e63fadc0eba7fa69e0ca56136c72a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Package config provides tools to load and validate configuration data for
// Traffic Ops API tests.
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"strings"
log "github.com/apache/trafficcontrol/lib/go-log"
"github.com/kelseyhightower/envconfig"
)
// Config reflects the structure of the test-to-api.conf file
type Config struct {
TrafficOps TrafficOps `json:"trafficOps"`
TrafficOpsDB TrafficOpsDB `json:"trafficOpsDB"`
Default Default `json:"default"`
UseIMS bool `json:"use_ims"`
// Sets whether or not to perform tests that must proxy to Perl
NoPerl bool `json:"noPerl"`
// Sets whether or not to perform tests that involve ISO generation
NoISO bool `json:"noISO"`
}
// TrafficOps - config section
type TrafficOps struct {
// URL - The point to the Traffic Ops instance being tested
URL string `json:"URL" envconfig:"TO_URL"`
// UserPassword - The Traffic Ops test user password hitting the API
UserPassword string `json:"password" envconfig:"TO_USER_PASSWORD"`
// User - The Traffic Ops Users
Users Users `json:"users"`
// Insecure - ignores insecure ssls certs that were self-generated
Insecure bool `json:"sslInsecure" envconfig:"SSL_INSECURE"`
}
// Users "users" section of the test-to-api.conf file
type Users struct {
// DisallowedUser - The Traffic Ops Disallowed user
Disallowed string `json:"disallowed" envconfig:"TO_USER_DISALLOWED"`
// ReadOnly - The Traffic Ops Read Only user
ReadOnly string `json:"readOnly" envconfig:"TO_USER_READ_ONLY"`
// Operations - The Traffic Ops Operations user
Operations string `json:"operations" envconfig:"TO_USER_OPERATIONS"`
// AdminUser - The Traffic Ops Admin user
Admin string `json:"admin" envconfig:"TO_USER_ADMIN"`
// PortalUser - The Traffic Ops Portal user
Portal string `json:"portal" envconfig:"TO_USER_PORTAL"`
// FederationUser - The Traffic Ops Federation user
Federation string `json:"federation" envconfig:"TO_USER_FEDERATION"`
// Extension - The Traffic Ops Extension user
Extension string `json:"extension" envconfig:"TO_USER_EXTENSION"`
}
// TrafficOpsDB - config section
type TrafficOpsDB struct {
// Name - Traffic Ops Database name where the test data will be setup
Name string `json:"dbname" envconfig:"TODB_NAME"`
// Hostname - Traffic Ops Database hostname where Postgres is running
Hostname string `json:"hostname" envconfig:"TODB_HOSTNAME"`
// User - database user that Traffic Ops is using to point to the 'to_test' database
User string `json:"user" envconfig:"TODB_USER"`
// Password - database password for the User above
Password string `json:"password" envconfig:"TODB_PASSWORD"`
// Port - Postgres port running that has the to_test schema
Port string `json:"port" envconfig:"TODB_PORT"`
// DBType - will be 'Pg' by default but tells the Golang database driver
// the database type
DBType string `json:"type" envconfig:"TODB_TYPE"`
// SSL - Flag that tells the database driver that the Postgres instances has TLS enabled
SSL bool `json:"ssl" envconfig:"TODB_SSL"`
// Description - database description
Description string `json:"description" envconfig:"TODB_DESCRIPTION"`
}
// Default - config section
type Default struct {
Session Session `json:"session"`
Log Locations `json:"logLocations"`
IncludeSystemTests bool `json:"includeSystemTests"`
}
// Session - config section
type Session struct {
TimeoutInSecs int `json:"timeoutInSecs" envconfig:"SESSION_TIMEOUT_IN_SECS"`
}
// Locations - reflects the structure of the database.conf file
type Locations struct {
Debug string `json:"debug"`
Event string `json:"event"`
Error string `json:"error"`
Info string `json:"info"`
Warning string `json:"warning"`
}
// LoadConfig - reads the config file into the Config struct
func LoadConfig(confPath string) (Config, error) {
var cfg Config
confBytes, err := ioutil.ReadFile(confPath)
if err != nil {
return cfg, fmt.Errorf("failed to read CDN configuration: %v", err)
}
err = json.Unmarshal(confBytes, &cfg)
if err != nil {
return cfg, fmt.Errorf("failed to parse configuration from '%s': %v", confPath, err)
}
if err := validate(confPath, cfg); err != nil {
return cfg, fmt.Errorf("failed to validate configuration:\n%v", err)
}
if err := envconfig.Process("traffic-ops-client-tests", &cfg); err != nil {
return cfg, fmt.Errorf("failed to parse configuration from environment: %v", err)
}
return cfg, nil
}
type multiError []error
func (me multiError) Error() string {
var sb strings.Builder
for _, e := range me {
fmt.Fprintln(&sb, e)
}
return sb.String()
}
// validate all required fields in the config.
func validate(confPath string, config Config) error {
var errs multiError
var f string
f = "TrafficOps"
toTag, ok := getStructTag(config, f)
if !ok {
errs = append(errs, fmt.Errorf("'%s' must be configured in %s", toTag, confPath))
}
if config.TrafficOps.URL == "" {
f = "URL"
tag, ok := getStructTag(config.TrafficOps, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if config.TrafficOps.Users.Disallowed == "" {
f = "Disallowed"
tag, ok := getStructTag(config.TrafficOps.Users, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if config.TrafficOps.Users.ReadOnly == "" {
f = "ReadOnly"
tag, ok := getStructTag(config.TrafficOps.Users, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if config.TrafficOps.Users.Operations == "" {
f = "Operations"
tag, ok := getStructTag(config.TrafficOps.Users, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if config.TrafficOps.Users.Admin == "" {
f = "Admin"
tag, ok := getStructTag(config.TrafficOps.Users, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if config.TrafficOps.Users.Portal == "" {
f = "Portal"
tag, ok := getStructTag(config.TrafficOps.Users, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if config.TrafficOps.Users.Federation == "" {
f = "Federation"
tag, ok := getStructTag(config.TrafficOps.Users, f)
if !ok {
errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
}
errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
}
if len(errs) > 0 {
return errs
}
return nil
}
func getStructTag(thing interface{}, fieldName string) (string, bool) {
var tag string
var ok bool
t := reflect.TypeOf(thing)
if t != nil {
if f, ok := t.FieldByName(fieldName); ok {
tag = f.Tag.Get("json")
return tag, ok
}
}
return tag, ok
}
// ErrorLog - critical messages
func (c Config) ErrorLog() log.LogLocation {
return log.LogLocation(c.Default.Log.Error)
}
// WarningLog - warning messages
func (c Config) WarningLog() log.LogLocation {
return log.LogLocation(c.Default.Log.Warning)
}
// InfoLog - information messages
func (c Config) InfoLog() log.LogLocation {
return log.LogLocation(c.Default.Log.Info)
}
// DebugLog - troubleshooting messages
func (c Config) DebugLog() log.LogLocation {
return log.LogLocation(c.Default.Log.Debug)
}
// EventLog - access.log high level transactions
func (c Config) EventLog() log.LogLocation {
return log.LogLocation(c.Default.Log.Event)
}