Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- /api/1.2/current_stats
- /api/1.1/osversions

- Traffic Ops API Routing Blacklist: via the `routing_blacklist` field in `cdn.conf`, enable certain whitelisted Go routes to be handled by Perl instead (via the `perl_routes` list) in case a regression is found in the Go handler, and explicitly disable any routes via the `disabled_routes` list. Requests to disabled routes are immediately given a 503 response. Both fields are lists of Route IDs, and route information (ID, version, method, path, and whether or not it can bypass to Perl) can be found by running `./traffic_ops_golang --api-routes`. To disable a route or have it bypassed to Perl, find its Route ID using the previous command and put it in the `disabled_routes` or `perl_routes` list, respectively.
- To support reusing a single riak cluster connection, an optional parameter is added to riak.conf: "HealthCheckInterval". This options takes a 'Duration' value (ie: 10s, 5m) which affects how often the riak cluster is health checked. Default is currently set to: "HealthCheckInterval": "5s".
- Added a new Go db/admin binary to replace the Perl db/admin.pl script which is now deprecated and will be removed in a future release. The new db/admin binary is essentially a drop-in replacement for db/admin.pl since it supports all of the same commands and options; therefore, it should be used in place of db/admin.pl for all the same tasks.
- Added an API 1.4 endpoint, /api/1.4/cdns/dnsseckeys/refresh, to perform necessary behavior previously served outside the API under `/internal`.
Expand Down
15 changes: 14 additions & 1 deletion docs/source/admin/traffic_ops.rst
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ The script takes no options other than the ones accepted by `Hypnotoad <https://

traffic_ops_golang
------------------
``traffic_ops_golang [--version] [--plugins] --cfg CONFIG_PATH --dbcfg DB_CONFIG_PATH --riakcfg TRAFFIC_VAULT_CONFIG_PATH``
``traffic_ops_golang [--version] [--plugins] [--api-routes] --cfg CONFIG_PATH --dbcfg DB_CONFIG_PATH --riakcfg TRAFFIC_VAULT_CONFIG_PATH``

.. option:: --cfg CONFIG_PATH

Expand All @@ -278,6 +278,12 @@ traffic_ops_golang

.. note:: This only accounts for the plugins for the Go version, extensions to the `Legacy Perl Script`_ are not accounted for.

.. option:: --api-routes

Print information about all API routes and exit. If also used with the :option:`--cfg` option, also print out the configured routing blacklist information from `cdn.conf`_.

.. note:: This only accounts for routes in the Go version, API routes in Perl but not in Go are not included.

.. option:: --riakcfg TRAFFIC_VAULT_CONFIG_PATH

This **mandatory** command line flag specifies the absolute or relative path to a configuration file used by Traffic Ops to establish connections to Traffic Vault - `riak.conf`_
Expand Down Expand Up @@ -409,6 +415,13 @@ This file deals with the configuration parameters of running Traffic Ops itself.
.. warning:: OAuth support in Traffic Ops is still in its infancy, so most users are advised to avoid defining this field without good cause.

:write_timeout: An optional timeout in seconds set on handlers. After reading a request's header, the server will have this long to send back a response. If set to zero, there is no timeout. Default if not specified is zero.
:routing_blacklist: Optional configuration for explicitly routing requests to TO-Perl via ``perl_routes`` (only routes that are hardcoded to be able to bypass to TO-Perl -- not all Go routes can be bypassed to Perl) or explicitly disabling any routes via ``disabled_routes``.

.. versionadded:: 4.0

:perl_routes: A list of API route IDs to be handled by TO-Perl (rather than by the matching routes in ``traffic_ops_golang``). This list can only contain IDs for routes that are on the hardcoded (within ``traffic_ops_golang``) whitelist of routes that can be bypassed to TO-Perl. This configuration is meant to allow falling back to TO-Perl for routes that have been rewritten to TO-Go but have been found to contain regressions. In order to find which routes can be bypassed to TO-Perl, run ``./traffic_ops_golang`` using the :option:`--api-routes` option. This will print out information about all API routes in ``traffic_ops_golang``, including route IDs, paths, and whether or not routes can be bypassed to Perl. In general, the whitelist will contain only routes that have recently been rewritten to Go but not yet included in a release, and only if the Go route has not deviated from its corresponding Perl route in a way that would make it dangerous to fall back to. This whitelist should be expected to change as Go routes become "vetted" in a release. Once TO-Perl is removed, this field will be removed/ignored.
:disabled_routes: A list of API route IDs to disable. Requests matching these routes will receive a 503 response. To find the route ID for a given path you would like to disable, run ``./traffic_ops_golang`` using the :option:`--api-routes` option to view all the route information, including route IDs and paths.
:ignore_unknown_routes: If ``false`` (default) return an error and prevent startup if unknown route IDs are found. Otherwise, log a warning and continue startup.

Example cdn.conf
''''''''''''''''
Expand Down
5 changes: 5 additions & 0 deletions traffic_ops/app/conf/cdn.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
},
"whitelisted_oauth_urls": [],
"oauth_client_secret": "",
"routing_blacklist": {
"ignore_unknown_routes": false,
"perl_routes": [],
"disabled_routes": []
},
"profiling_enabled": false
},
"cors" : {
Expand Down
4 changes: 3 additions & 1 deletion traffic_ops/traffic_ops_golang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ Now we need to create the Go endpoint.

##### Getting a "Handle" on Routes

Open [routes.go](./routing/routes.go). Routes are defined in the `Routes` function, of the form `{version, method, path, handler}`. Notice the path can contain variables, of the form `/{var}/`. These variables will be made available to your handler.
Open [routes.go](./routing/routes.go). Routes are defined in the `Routes` function, of the form `{version, method, path, handler, ID}`. Notice the path can contain variables, of the form `/{var}/`. These variables will be made available to your handler.

NOTE: Route IDs are immutable and unique. DO NOT change the ID of an existing Route; otherwise, existing configurations may break. New Route IDs can be any integer between 0 and 2147483647 (inclusive), as long as it's unique.

##### Creating a Handler

Expand Down
45 changes: 45 additions & 0 deletions traffic_ops/traffic_ops_golang/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type ConfigTrafficOpsGolang struct {
RiakPort *uint `json:"riak_port"`
WhitelistedOAuthUrls []string `json:"whitelisted_oauth_urls"`
OAuthClientSecret string `json:"oauth_client_secret"`
RoutingBlacklist `json:"routing_blacklist"`

// CRConfigUseRequestHost is whether to use the client request host header in the CRConfig. If false, uses the tm.url parameter.
// This defaults to false. Traffic Ops used to always use the host header, setting this true will resume that legacy behavior.
Expand All @@ -108,6 +109,14 @@ type ConfigTrafficOpsGolang struct {
CRConfigEmulateOldPath bool `json:"crconfig_emulate_old_path"`
}

// RoutingBlacklist contains the list of route IDs that will be handled by TO-Perl, a list of route IDs that are disabled,
// and whether or not to ignore unknown routes.
type RoutingBlacklist struct {
IgnoreUnknownRoutes bool `json:"ignore_unknown_routes"`
Comment thread
ocket8888 marked this conversation as resolved.
Outdated
PerlRoutes []int `json:"perl_routes"`
DisabledRoutes []int `json:"disabled_routes"`
}
Comment thread
ocket8888 marked this conversation as resolved.
Outdated

// ConfigTO contains information to identify Traffic Ops in a network sense.
type ConfigTO struct {
BaseURL *rfc.URL `json:"base_url"`
Expand Down Expand Up @@ -163,6 +172,16 @@ type ConfigInflux struct {
Secure *bool `json:"secure"`
}

// NewFakeConfig returns a fake Config struct with just enough data to view Routes.
func NewFakeConfig() Config {
c := Config{}
c.URL, _ = url.Parse("http://example.com")
c.Secrets = append(c.Secrets, "foo")
c.BackendMaxConnections = make(map[string]int, 1)
c.BackendMaxConnections["mojolicious"] = 42
return c
}

const DefaultLDAPTimeoutSecs = 60
const DefaultDBQueryTimeoutSecs = 20

Expand Down Expand Up @@ -386,9 +405,35 @@ func ParseConfig(cfg Config) (Config, error) {
return Config{}, fmt.Errorf(errStr)
}

if err := ValidateRoutingBlacklist(cfg.RoutingBlacklist); err != nil {
return Config{}, err
}

return cfg, nil
}

func ValidateRoutingBlacklist(blacklist RoutingBlacklist) error {
seenPerlIDs := make(map[int]struct{}, len(blacklist.PerlRoutes))
for _, id := range blacklist.PerlRoutes {
if _, found := seenPerlIDs[id]; !found {
seenPerlIDs[id] = struct{}{}
} else {
return fmt.Errorf("route ID %d is listed multiple times in perl_routes", id)
}
}
seenDisabledIDs := make(map[int]struct{}, len(blacklist.DisabledRoutes))
for _, id := range blacklist.DisabledRoutes {
if _, foundInPerl := seenPerlIDs[id]; foundInPerl {
return fmt.Errorf("route ID %d cannot be listed in both perl_routes and disabled_routes", id)
} else if _, found := seenDisabledIDs[id]; !found {
seenDisabledIDs[id] = struct{}{}
} else {
return fmt.Errorf("route ID %d is listed multiple times in disabled_routes", id)
}
}
return nil
}

func GetLDAPConfig(LDAPConfPath string) (bool, *ConfigLDAP, error) {
LDAPConfBytes, err := ioutil.ReadFile(LDAPConfPath)
if err != nil {
Expand Down
77 changes: 77 additions & 0 deletions traffic_ops/traffic_ops_golang/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ const (
"read_header_timeout" : 60,
"write_timeout" : 60,
"idle_timeout" : 60,
"routing_blacklist": {
"ignore_unknown_routes": true,
"perl_routes": [1, 2, 3],
"disabled_routes": [4, 5, 6]
},
"log_location_error": "stderr",
"log_location_warning": "stdout",
"log_location_info": "stdout",
Expand Down Expand Up @@ -262,3 +267,75 @@ func TestLoadConfig(t *testing.T) {
t.Error("Expected KeyPath() == /etc/pki/tls/private/localhost.key")
}
}

func TestValidateRoutingBlacklist(t *testing.T) {
type testCase struct {
Input RoutingBlacklist
ExpectErr bool
}
testCases := []testCase{
{
Input: RoutingBlacklist{
PerlRoutes: nil,
DisabledRoutes: nil,
},
ExpectErr: false,
},
{
Input: RoutingBlacklist{
PerlRoutes: []int{1, 2, 3},
DisabledRoutes: []int{4, 5, 6},
},
ExpectErr: false,
},
{
Input: RoutingBlacklist{
PerlRoutes: nil,
DisabledRoutes: []int{4, 5, 6},
},
ExpectErr: false,
},
{
Input: RoutingBlacklist{
PerlRoutes: []int{4, 5, 6},
DisabledRoutes: nil,
},
ExpectErr: false,
},
{
Input: RoutingBlacklist{
PerlRoutes: []int{1, 1, 3},
DisabledRoutes: []int{2, 2, 4},
},
ExpectErr: true,
},
{
Input: RoutingBlacklist{
PerlRoutes: []int{1, 1, 3},
DisabledRoutes: []int{4, 5, 6},
},
ExpectErr: true,
},
{
Input: RoutingBlacklist{
PerlRoutes: []int{1, 2, 3},
DisabledRoutes: []int{4, 4, 6},
},
ExpectErr: true,
},
{
Input: RoutingBlacklist{
PerlRoutes: []int{1, 2, 3},
DisabledRoutes: []int{1, 4, 5},
},
ExpectErr: true,
},
}
for _, tc := range testCases {
if err := ValidateRoutingBlacklist(tc.Input); err != nil && !tc.ExpectErr {
t.Errorf("Expected: no error, actual: %v", err)
} else if err == nil && tc.ExpectErr {
t.Errorf("Expected: non-nil error, actual: nil")
}
}
}
Loading