Traffic Ops

At its current stage in development, “Traffic Ops” actually refers to a concept with two implementations. The original Traffic Ops was written as a collection of Perl scripts based on the Mojolicious framework framework. At some point, the relatively poor performance and lack of knowledgeable developers as the project expanded became serious issues, and so for the past few years Traffic Ops has undergone a rewrite to Go.


Traffic Ops at its core is mainly a PostgreSQL database used to store configuration information for ATC, and a set of RESTful API endpoints for interacting with and manipulating that information. It also serves as the single point of authentication for ATC components (that is, when one hears “user” in an ATC context it nearly always means a “user” as configured in Traffic Ops) and provides interfaces to other ATC components by proxy. Additionally, there is some miscellaneous, at times obscure functionality to Traffic Ops, such as generating arbitrary Linux system images.

Software Requirements

Traffic Ops is only supported on CentOS 7+ systems (although many developers do use Mac OS with some success).

The two different implementations have different requirements, but they do share a few:

  • Goose (although the postinstall Perl script will install this if desired)

  • PostgreSQL 9.6.6 - the machine where (either implementation of) Traffic Ops is running must have the client tool set (e.g. psql(1)), but the actual database can be run anywhere so long as it is accessible.

  • openssl(1SSL) is recommended to generate server certificates, though not strictly required if certificates can be obtained by other means.

  • Some kind of SMTP server is required for certain Traffic Ops API endpoints to work, but for purposes unrelated to them an SMTP server is not required. CDN in a Box comes with a relayless SMTP server for testing (you can view the emails that Traffic Ops sends, but they aren’t sent anywhere outside CDN-in-a-Box).


Alternatively, development and testing can be done using CDN in a Box - albeit somewhat more slowly.

Perl Implementation Requirements

Most dependencies are managed by Carton 1.0.12, but there are some - outside of those shared with the Go implementation - that are not managed by that system.

  • Carton itself

  • Perl 5.10.1

  • libpcap and libpcap development library - usually libpcap-dev or libpcap-devel in your system’s native package manager.

  • libpq and libpq development library - usually libpq-dev or libpq-devel in your system’s native package manager.

  • The JSON Perl pod from CPAN

  • The JSON::PP Perl pod from CPAN

  • Developers should use Perltidy to format their Perl code.

    #118 Example Perltidy Configuration (usually in HOME/.perltidyrc)
    -wls="= + - / * ."
    -wrs=\"= + - / * .\"
    -wbb="% + - * / x != == >= <= =~ < > | & **= += *= &= <<= &&= -= /= |= + >>= ||= .= %= ^= x="

Go Implementation Requirements

  • Go 1.14 or later

  • If the system’s Go compiler doesn’t provide it implicitly, also note that all Go code in the ATC repository should be formatted using gofmt

All Go code dependencies are managed through the vendor/ directory and should thus be available without any extra work - and any new dependencies should be properly “vendored” into that same, top-level directory. Some dependencies have been “vendored” into traffic_ops/vendor and traffic_ops/traffic_ops_golang/vendor but the preferred location for new dependencies is under that top-level vendor/ directory.

Per the Go language standard’s authoritative source’s recommendation, all sub-packages of are treated as a part of the compiler, and so need not ever be “vendored” as though they were an external dependency. These dependencies are not listed explicitly here, so it is strongly advised that they be fetched using go-get(1) rather than downloaded by hand.


All new dependencies need to be subject to community review to ensure necessity (because it will be added in its entirety to the repository, after all) and license compliance via the developer mailing list <>.

Traffic Ops Project Tree Overview

  • traffic_ops/ - The root of the Traffic Ops project

    • app/ - Holds most of the Perl code base, though many of the files contained herein are also used by the Go implementation


      This directory is home to many things that no longer work as intended or have been superseded by other things - most notably code for the now-removed Traffic Ops UI. That does not, however, mean that they are safe to remove. The API code that is still relied upon today is deeply entangled with the UI code, and in a dynamic language like Perl it can be very dangerous to remove things, because it may not be apparent that something is broken until it’s already in production. So please don’t remove anything in here until we’re ready to excise the Perl implementation entirely.

      • bin/ - Directory for scripts and tools, cron(8) jobs, etc.

        • checks/ - Contains the Check Extensions scripts that are provided by default

        • db/ - Contains scripts that manipulate the database beyond the scope of setup, migration, and seeding

        • tests/ - Integration and unit test scripts for automation purposes - in general this has been superseded by traffic_ops/testing/api/1

      • conf/ - Aggregated configuration for Traffic Ops. For convenience, different environments for the app/db/admin tool are already set up

        • development/ - Configuration files for the “development” environment

        • integration/ - Configuration files for the “integration” environment

        • misc/ - Miscellaneous configuration files.

        • production/ - Configuration files for the “production” environment

        • test/ - Configuration files for the “test” environment

      • db/ - Database setup, seeding, and upgrade/downgrade helpers

        • migrations/ - Database migration files

        • tools/ - Contains helper scripts for easing upgrade transitions when selective data manipulation must be done to achieve a desirable state

      • lib/ - Contains the main handling logic for the Perl implementation

        • API/ - Mojolicious Controllers for the Traffic Ops API

        • Common/ - Code that is shared between both the Traffic Ops API and the now-removed Traffic Ops UI

        • Connection/ - Adapter definitions for connecting to external services

        • Extensions/ - Contains Data Source Extensions

        • Fixtures/ - Test-case fixture data for the “testing” environment1

          • Integration/ - Integration tests1

        • Helpers/ - Contains route handlers for the Traffic Stats-related endpoints

        • MojoPlugins/ - Mojolicious Plugins for common controller code

        • Schema/Result/ - Contains schema definitions generated from a constructed database for use with the DBIx Perl pod suite. These were machine-generated in 2016 and absolutely no one should be touching them ever again.

        • /Test - Common helpers for testing

        • UI/ - Mojolicious controllers for the now-removed Traffic Ops UI

        • Utils/ - Contains helpful utilities for certain objects and tasks

          • Helper/ - Common utilities for the Traffic Ops application

      • public/ - A directory from which files are served statically over HTTP by the Perl implementation. One common use-case is for hosting a Coverage Zone File for Traffic Router.

      • script/ - Mojolicious bootstrap/startup scripts.

      • t/ - Unit tests for both the API (in api/) and the now-removed Traffic Ops UI1

        • api/ - Unit tests for the API1

      • t_integration/ - High-level integration tests1

      • templates/ - Mojolicious Embedded Perl (template name.ep) files for the now-removed Traffic Ops UI

    • build/ - Contains files that are responsible for packaging Traffic Ops into an RPM file - and also for doing the same with ORT

    • client/ - The Go client library for Traffic Ops

    • etc/ - Configuration files for various systems associated with running production instances of Traffic Ops, which are installed under /etc by the Traffic Ops RPM

      • cron.d/ - Holds specifications for cron(8) jobs that need to be run periodically on Traffic Ops servers


        At least one of these jobs expects itself to be run on a server that has the Perl implementation of Traffic Ops installed under /opt/traffic_ops/. Nothing terrible will happen if that’s not true, just that it/they won’t work. Installation using the RPM will set up all of these kinds of things up automatically.

      • init.d/ - Contains the old, initscripts-based job control for Traffic Ops

      • logrotate.d/ - Specifications for the Linux logrotate(8) utility for Traffic Ops log files

      • profile.d/ - Sets up common environment variables for working with Traffic Ops

    • install/ - Contains all of the resources necessary for a full install of Traffic Ops

      • bin/ - Binaries related to installing Traffic Ops, as well as installing its prerequisites, certificates, and database

      • data/ - Contains things that need to be accessible by the running server for certain functionality - typically installed to /var/www/data by the RPM (hence the name).

      • etc/ - This directory left empty; it’s used to contain post-installation extensions and resources

      • lib/ - Contains libraries used by the various installation binaries

    • ort/ - Contains ORT and ATS configuration file-generation logic and tooling

    • testing/ - Holds utilities for testing the Traffic Ops API

    • traffic_ops_golang/ - The root of the Go implementation’s code-base


      The vast majority of subdirectories of traffic_ops/traffic_ops_golang/ contain handlers for the Traffic Ops API, and are named according to the endpoint they handle. What follows is a list of subdirectories of interest that have a special role (i.e. don’t handle a Traffic Ops API endpoint).

      • api/ - A library for use by Traffic Ops API handlers that provides helpful utilities for common tasks like obtaining a database transaction handle or accessing Traffic Ops configuration

      • auth/ - Contains definitions of privilege levels and access control code used in routing and provides a library for dealing with password and token-based authentication

      • config/ - Defines configuration structures and methods for reading them in from files

      • dbhelpers/ - Assorted utilities that provide functionality for common database tasks, e.g. “Get a user by email”

      • plugin/ - The Traffic Ops plugin system, with examples

      • riaksvc/ - In addition to handling routes that deal with storing secrets in or retrieving secrets from Traffic Vault, this package provides a library of functions for interacting with Traffic Vault for other handlers to use.

      • routing/ - Contains logic for mapping all of the Traffic Ops API endpoints to their handlers, as well as proxying requests back to the Perl implementation and managing plugins, and also provides some wrappers around registered handlers that set common HTTP headers and connection options

      • swaggerdocs/ A currently abandoned attempt at defining the Traffic Ops API using Swagger - it may be picked up again at some point in the (distant) future

      • tenant/ - Contains utilities for dealing with Tenantable resources, particularly for checking for permissions

      • tocookie/ - Defines the method of generating the mojolicious cookie used by Traffic Ops for authentication

      • vendor/ - contains “vendored” Go packages from third party sources

    • vendor/ - contains “vendored” Go packages from third party sources


The app/db/admin binary is for use in managing the Traffic Ops database tables. This essentially serves as a front-end for Goose.


For proper resolution of configuration and SOL statement files, it’s recommended that this binary be run from the app directory


db/admin [options] command

Options and Arguments


An optional environment specification that causes the database configuration to be read out of the corresponding section of the app/db/dbconf.yml configuration file. One of:

  • development

  • integration

  • production

  • test

(Default: development)


admin sets this to the value of the environment as specified by --env (Default: development)


The command specifies the operation to be performed on the database. It must be one of:


Creates the database for the current environment


Creates the user defined for the current environment


Displays the database version that results from the current sequence of migrations


Rolls back a single migration from the current version


Drops the database for the current environment


Drops the user defined for the current environment


Sets up the database for the current environment according to the SQL statements in app/db/create_tables.sql


Runs a migration on the database for the current environment


Patches the database for the current environment using the SQL statements from the app/db/patches.sql


Rolls back the most recently applied migration, then run it again


Creates the user defined for the current environment, drops the database for the current environment, creates a new one, loads the schema into it, and runs a single migration on it


Reverse engineers the app/lib/Schema/Result/* files from the environment database


Executes the SQL statements from the app/db/seeds.sql file for loading static data


Displays a list of all users registered with the PostgreSQL server


Prints the status of all migrations


Performs a migration on the database for the current environment, then seeds it and patches it using the SQL statements from the app/db/patches.sql file

#119 Example Usage
db/admin --env=test reset

The environments are defined in the traffic_ops/app/db/dbconf.yml file, and the name of the database generated will be the name of the environment for which it was created.

Installing The Developer Environment

To install the Traffic Ops Developer environment:

  1. Clone the Traffic Control repository from GitHub. In most cases it is best to clone this directly into GOPATH/src/, as otherwise the Go implementation will not function properly.

  2. Install the local dependencies using Carton.

    #120 Install Perl Dependencies
    # assuming current working directory is the repository root
    cd traffic_ops/app
  3. Install any required Go dependencies - the suggested method is using go-get(1).

    #121 Install Go Development Dependencies
    # assuming current working directory is the repository root
    go get -v ./lib/... ./traffic_ops/traffic_ops_golang/...
  4. Set up a role (user) in PostgreSQL

  5. Use the reset and upgrade commands of admin (see app/db/admin for usage) to set up the traffic_ops database(s).

  6. Run the traffic_ops/install/bin/postinstall script, it will prompt for information like the default user login credentials

  7. To run Traffic Ops, follow the instructions in Running.

Test Cases

Perl Tests

Use prove (should be installed with Perl) to execute test cases1. Execute after a carton install of all required dependencies:

  • To run the Unit Tests: prove -qrp  app/t/

  • To run the Integration Tests: prove -qrp app/t_integration/

Go Tests

Many (but not all) endpoint handlers and utility packages in the Go code-base define Go unit tests that can be run with go-test(1). There are integration tests for the Traffic Ops Go client in traffic_ops/testing/api/.

#122 Sample Run of Go Unit Tests
cd traffic_ops/traffic_ops_golang

# run just one test
go test ./about

# run all of the tests
go test ./...

There are a few prerequisites to running the Go client integration tests:

  • A PostgreSQL server must be accessible and have a Traffic Ops database schema set up (though not necessarily populated with anything).

  • A running Traffic Ops Go implementation instance must be accessible - it shouldn’t be necessary to also be running the Perl implementation.


    For testing purposes, SSL certificates are not verified, so self-signed certificates will work fine.


    It is highly recommended that the Traffic Ops instance be run on the same machine as the integration tests, as otherwise network latency can cause the tests to exceed their threshold time limit of ten minutes.

The integration tests are run using go-test(1), with two configuration options available.


It should be noted that the integration tests will output thousands of lines of highly repetitive text not directly related to the tests its running at the time - even if the -v flag is not passed to go-test(1). This problem is tracked by Issue #4017.


Running the tests will wipe the connected database clean, so do not ever run it on an instance of Traffic Ops that holds meaningful data.

--cfg CONFIG

Specify the path to the Test Configuration File. If not specified, it will attempt to read a file named traffic-ops-test.conf in the working directory.

See also

Configuring the Integration Tests for a detailed explanation of the format of this configuration file.

--fixtures FIXTURES

Specify the path to a file containing static data for the tests to use. This should almost never be used, because many of the tests depend on the data having a certain content and structure. If not specified, it will attempt to read a file named tc-fixtures.json in the working directory.

Configuring the Integration Tests

Configuration is mainly done through the configuration file passed as --cfg, but is also available through the following environment variables.


Sets the timeout of requests made to the Traffic Ops instance, in seconds.


An utterly cosmetic variable which, if set, gives a description of the PostgreSQL database to which the tests will connect. This has no effect except possibly changing one line of debug output.


If set, will define the FQDN at which the PostgreSQL server to be used by the tests resides2.


If set, will define the name of the database to which the tests will connect2.


If set, defines the password to use when authenticating with the PostgreSQL server.


If set, defines the port on which the PostgreSQL server listens2.


If set, must be one of the following values:


The PostgreSQL server to which the tests will connect uses SSL on the port on which it will be contacted.


The PostgreSQL server to which the tests will connect does not use SSL on the port on which it will be contacted.


If set, tells the database driver used by the tests the kind of SQL database to which they are connecting2. This author has no idea what will happen if this is set to something other than Pg, but it’s possible the tests will fail to run. Certainly never do it.


If set, defines the user as whom to authenticate with the PostgreSQL server.


If set, will define the URL at which the Traffic Ops instance is running - including port number.


If set, will define the name of a user with the “admin” Role that will be created by the tests3.


If set, will define the name of a user with the “disallowed” Role that will be created by the tests3.


If set, will define the name of a user with the “extension” Role that will be created by the tests3.


Due to legacy constraints, the only truly safe value for this is extension - anything else could cause the tests to fail.


If set, will define the name of a user with the “federation” Role that will be created by the tests3.


If set, will define the name of a user with the “operations” Role that will be created by the tests3.


If set, will define the password used by all users created by the tests. This does not need to be the password of any pre-existing user.


If set, will define the name of a user with the “portal” Role that will be created by the tests3.


If set, will define the name of a user with the “read-only” Role that will be created by the tests3.

Test Configuration File

The configuration file for the tests (defined by --cfg) is a JSON-encoded object with the following properties.


Many of these configuration options are overridden by variables in the execution environment. Where this is a problem, there is an associated warning. In general, this issue is tracked by Issue #3975.


An object containing sub-objects relating to default configuration settings for connecting to external resources during testing


An object containing key/value pairs where the keys are log levels and each associated value is the file location to which logs of that level will be written. The allowed values respect the reserved special names used by the package. Omitted keys are treated as though their values were null, in which case that level is written to /dev/null. The allowed keys are:

  • debug

  • error

  • event

  • info

  • warning


An object containing key/value pairs that define the default settings used by Traffic Ops “session” connections


At the time of this writing this is the only meaningful configuration option that may be present under session. It specifies the timeouts used by client connections during testing as an integer number of seconds. The default if not specified (or overridden) is 0, meaning no limit.


This configuration is overridden by SESSION_TIMEOUT_IN_SECS.


An object containing information that defines the running Traffic Ops instance to use in testing.


This password will be used for all created users used by the test suite - it does not need to be the password of any pre-existing user. The default if not specified (or overridden) is an empty string, which may or may not cause problems.


This is overridden by TO_USER_PASSWORD.


The network location of the running Traffic Ops server, including schema, hostname and optionally port number e.g. https://localhost:6443.


This is overridden by TO_URL.


An object containing key-value pairs where the keys are the names of Roles and the values are the usernames of users that will be created with the associated Role for testing purposes. There are very few good reasons why the values should not just be the same as the keys. The default for any missing (and not overridden) key is the empty string which is won’t work so please don’t leave any undefined. The allowed keys are:


An object containing information that defines the database to use in testing2.


The name of the database to which the tests will connect2.


This is overridden by TODB_NAME.


An utterly cosmetic option that need not exist at all which, if set, gives a description of the database to which the tests will connect. This has no effect except possibly changing one line of debug output.


This is overridden by TODB_DESCRIPTION


The FQDN of the server on which the database is running2


This is overridden by TODB_HOSTNAME.


The password to use when authenticating with the database


This is overridden by TODB_PASSWORD.


The port on which the database listens for connections2 - as a string


This is overridden by TODB_PORT.


The “type” of database being used2. This should never be set to anything besides "Pg", anything else results in undefined behavior (although it’s equally possible that it simply won’t have any effect).


This is overridden by TODB_TYPE.


An optional boolean value that defines whether or not the database uses SSL encryption for its connections - default if not specified (or overridden) is false


This is overridden by TODB_SSL.


The name of the user as whom to authenticate with the database


This is overridden by TODB_USER.

Writing New Endpoints

All new Traffic Ops API endpoints should be written in Go, so writing endpoints for the Perl implementation is not discussed here. Furthermore, most new endpoints are accompanied by database schema changes which necessitate a new migration under traffic_ops/app/db/migrations and database best-practices are not discussed in this section.

See also

This section contains a quick overview of API endpoint development; for the full guidelines for API endpoints, consult API Guidelines.

The first thing to consider when writing a new endpoint is what the requests it will serve will look like. It’s recommended that new endpoints avoid using “path parameters” when possible, and instead try to utilize request bodies and/or query string parameters. For example, instead of /foos/{{ID}} consider simply /foos with a supported id query parameter. The request methods should be restricted to the following, and respect each method’s associated meaning.


Removes a resource or one or more of its representations from the server. This should always be the method used when deleting objects.


Retrieves a representation of some resource. This should always be used for read-only operations and note that the requesting client never expects the state of the server to change as a result of a request using the GET method.


Requests that the server process some passed data. This is used most commonly to create new objects on the server, but can also be used more generally e.g. with a request for regenerating encryption keys. Although this isn’t strictly creating new API resources, it does change the state of the server and so this is more appropriate than GET.


Places a new representation of some resource on the server. This is typically used for updating existing objects. For creating new representations/objects, use POST instead. When using PUT note that clients expect it to be idempotent, meaning that subsequent identical PUT requests should result in the same server state. What this means is that it’s standard to require that all of the information defining a resource be provided for each request even if the vast majority of it isn’t changing.

The HEAD and OPTIONS request methods have default implementations for any properly defined Traffic Ops API route, and so should almost never be defined explicitly. Other request methods (e.g. CONNECT) are currently unused and ought to stay that way for the time being.


Utilizing the PATCH method is unfeasible at the time of this writing but progress toward supporting it is being made, albeit slowly in the face of other priorities.

The final step of creating any Traffic Ops API endpoint is to write documentation for it. When doing so, be sure to follow all of the guidelines laid out in Documentation Guidelines. If documentation doesn’t exist for new functionality then it has accomplished nothing because no one using Traffic Control will know it exists. Omitted documentation is how a project winds up with a dozen different API endpoints that all do essentially the same thing.

Framework Options

The Traffic Ops code base offers two basic frameworks for defining a new endpoint. Either one may be used at the author’s discretion (or even neither if desired and appropriate - though that seems unlikely).

Generic “CRUDer”

The “Generic ‘CRUDer’”, as it’s known, is a pattern of API endpoint development that principally involves defining a type that implements the interface. A description of what that entails is best left to the actual GoDoc documentation.

See also

The,,, and helpers are often used to provide the default operations of creating, deleting, reading, and updating objects, respectively. When the API endpoint being written is only meant to perform these basic operations on an object or objects stored in the database, these should be totally sufficient.

This method offers a lot of functionality “out-of-the-box” as compared to the APIInfo method, but because of that is also restrictive. For example, it is not possible to write an endpoint that returns data not encoded as JSON using this method. That’s an uncommon use-case, but not unheard-of.

This method is best used for basic creation, reading, update, and deletion operations performed on simple objects with no structural differences across API versions.


Endpoint handlers can also be defined by simply implementing the net/http.HandlerFunc interface. The net/http.Request reference passed into such handlers provides identifying information for the authenticated user (where applicable) in its context.

To easily obtain the information needed to identify a user and their associated permissions, as well as server configuration information and a database transaction handle, authors should use the function which will return all of that information in a single structure as well as any errors encountered during the process and an appropriate HTTP response code in case of such errors.

This method offers fine control over the endpoint’s logic, but tends to be much more verbose than the endpoints written using the Generic “CRUDer” method. For example, a handler for retrieving an object from the database and returning it to the requesting client encoded as JSON can be twenty or more lines of code, whereas a single call to provides equivalent functionality.

This method is best used when requests are meant to have extensive side-effects, are performed on unusually structured objects, need fine control of the HTTP headers/options, or operate on objects that have different structures or meanings across API versions.

Rewriting a Perl Endpoint

When rewriting endpoints from Perl, some special considerations must be taken.

  • Any rules and guidelines herein outlined that are broken by the Perl handler must also be broken in the rewritten Go handler to maintain compatibility within the API. New features can be added in the latest unreleased version of the API so long as they are appropriately documented, but avoid the temptation to fix things that seem broken. Such changes are best left to re-implementation of the API in a subsequent major version. The exceptions to this rule are if the broken behavior constitutes a security vulnerability (in which case be sure to follow the instructions on the Apache Software Foundation security page) or if it happens in the event of a server or client error. For example, many Perl handlers will spit out an HTML page in the event of a server-side error while the standard behavior of the Traffic Ops API in such cases is to return the appropriate HTTP response code and a response body containing a JSON-encoded alerts object describing the nature of the error.

  • Mark newly rewritten endpoints in their traffic_ops/traffic_ops_golang/routing/routes.go definition with perlBypass to ensure that upon upgrading it is possible to configure the server to fall back on the Perl implementation. That way, any erroneous rewrites that wind up in production environments can be quickly bypassed in favor of the old, known-to-be-working version.

  • The Perl handlers support any combination of optional trailing / and .json on endpoint routes, and rewritten route definitions ought to support that. For example, the endpoint /foo can with equal validity from the Perl implementation’s perspective as /foo.json, /foo/, /foo.json/ (for some reason), and even (horrendously) as /foo/.json.

  • It’s possible that a route definition for the newly rewritten route already exists, explicitly defining a proxy to the Perl implementation using handlerToFunc(proxyHandler) to avoid collisions with later-defined routes. These will need to be deleted in order for the route to be properly handled.


Both the Perl and Go implementation support different kinds of extensions.

What’s typically meant by “extension” in the context of Traffic Ops is a Check Extensions which provides data for server “checks” which can be viewed in Traffic Portal under Monitor ‣ Cache Checks. This type of extension need not know nor even care which implementation it is being used with, as it interacts with Traffic Ops through the Traffic Ops API. These are described in Legacy Perl Extensions as their description remains rather Perl-centric, but in principle their operation is not limited to the context of the Perl Implementation.

Both Perl and Go also support overrides or new definitions for non-standard Traffic Ops API routes. It is strongly recommended that no Perl-based extensions of this type be written, but for posterity they are described in Legacy Perl Extensions. The Go implementation refers to this type of “extension” as a “plugin,” and they are described in Go Plugins.

Go Plugins

A plugin is defined by a Go source file in the traffic_ops/traffic_ops_golang/plugin directory, which is expected to be named plugin name.go. A plugin is registered to Traffic Ops by a call to in the source file’s special init function.

A plugin is only enabled at runtime if its name is present in the cdn.conf file’s traffic_ops_golang.plugins array.

Each plugin may also define any, all, or none of the lifecycle hooks provided: load, startup, and onRequest


The load function of a plugin, if defined, needs to implement the interface, and will be run when the server starts and after configuration has been loaded. It will be passed the plugins own configuration as it was defined in the cdn.conf file’s traffic_ops_golang.plugin_config map.


The onRequest function of a plugin, if defined, needs to implement the interface, and will be called on every request made to the Traffic Ops API. Because of this, it’s imperative that the function exit as soon as possible. Note that once one plugin reports that it has served the request, no others will be tried. The order in which plugins are tried is defined by their order in the traffic_ops_golang.plugins array of the cdn.conf configuration file.

See also

It’s very common for this function to behave like a Traffic Ops API endpoint, so when writing a plugin it may be useful to review Writing New Endpoints.


Like load, the startup function of a plugin, if defined, will be called when the server starts and after configuration has been loaded. Unlike load, however, this function should implement the interface and will be passed in the entirety of the server’s configuration, including its own configuration and any shared plugin configuration data as defined in the cdn.conf file’s traffic_ops_golang.plugin_shared_config map.


An example “Hello World” plugin that serves the /_hello request path by just writing “Hello World” in the body of a 200 OK response back to the client is provided in traffic_ops/traffic_ops_golang/plugin/hello_world.go:

package plugin

   Licensed 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

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   See the License for the specific language governing permissions and
   limitations under the License.

import (

func init() {
    AddPlugin(10000, Funcs{onRequest: hello}, "example HTTP plugin", "1.0.0")

const HelloPath = "/_hello"

func hello(d OnRequestData) IsRequestHandled {
    if !strings.HasPrefix(d.R.URL.Path, HelloPath) {
        return RequestUnhandled
    d.W.Header().Set("Content-Type", "text/plain")
    d.W.Write([]byte("Hello, World!"))
    return RequestHandled

Legacy Perl Extensions

Traffic Ops Extensions are a way to enhance the basic functionality of Traffic Ops in a customizable manner. There are two types of extensions:

Check Extensions

These allow you to add custom checks to the Monitor ‣ Cache Checks view.

Data Source Extensions

These allow you to add statistic sources for the graph views and APIs.

Extensions are managed using the $TO_HOME/bin/extensions command line script

See also

For more information see Managing Traffic Ops Extensions.

Extensions at Runtime

The search path for Data Source Extensions depends on the configuration of the PERL5LIB environment variable, which is pre-configured in the Traffic Ops start scripts. All Check Extensions must be located in $TO_HOME/bin/checks

#123 Example PERL5LIB Configuration
export PERL5LIB=/opt/traffic_ops_extensions/private/lib/Extensions:/opt/traffic_ops/app/lib/Extensions/TrafficStats

To prevent Data Source Extensions namespace collisions within Traffic Ops all Data Source Extensions should follow the package naming convention ‘Extensions::<ExtensionName>

Traffic Ops accesses each extension through the addition of a URL route as a custom hook. These routes will be defined in a file called that should be present in the top directory of your Extension. The routes that are defined should follow the Mojolicious route conventions.

Development Configuration

To incorporate any custom Data Source Extensions during development set your PERL5LIB environment variable with any number of colon-separated directories with the understanding that the PERL5LIB search order is from left to right through this list. Once Perl locates your custom route or Perl package/class it ‘pins’ on that class or Mojolicious Route and doesn’t look any further, which allows for the developer to override Traffic Ops functionality.


As progress continues on moving Traffic Ops to run entirely in Go, the number of passing tests has steadily decreased. This means that the tests are not a reliable way to test Traffic Ops, as they are expected to fail more and more as functionality is stripped from the Perl codebase.


The Traffic Ops instance must be using the same PostgreSQL database that the tests will use.


This does not need to match the name of any pre-existing user.