tree: e8aa1352978539f2c78ef3da1233e49ed3d395fa [path history] [tgz]
  1. about/
  2. api/
  3. apiriak/
  4. apitenant/
  5. asn/
  6. ats/
  7. auth/
  8. cachegroup/
  9. cdn/
  10. cdnfederation/
  11. config/
  12. coordinate/
  13. crconfig/
  14. dbhelpers/
  15. deliveryservice/
  16. deliveryservicesregexes/
  17. division/
  18. hwinfo/
  19. login/
  20. monitoring/
  21. origin/
  22. parameter/
  23. physlocation/
  24. ping/
  25. profile/
  26. profileparameter/
  27. region/
  28. riaksvc/
  29. role/
  30. server/
  31. staticdnsentry/
  32. status/
  33. steeringtargets/
  34. swaggerdocs/
  35. systeminfo/
  36. tenant/
  37. test/
  38. tocookie/
  39. types/
  40. user/
  41. vendor/
  42. .gitignore
  43. README.md
  44. routes.go
  45. routing.go
  46. routing_test.go
  47. traffic_ops_golang.go
  48. urisigning.go
  49. urisigning_test.go
  50. wrappers.go
  51. wrappers_test.go
traffic_ops/traffic_ops_golang/README.md

Prequisites

To run traffic_ops_golang proxy locally the following prerequisites are needed:

vendoring and building

vendoring

We treat golang.org/x as apart of the Go compiler so that means that we still vendor application dependencies for stability and reproducible builds. This is a helpful tool for managing dependencies https://github.com/govend/govend

building

To download the remaining golang.org/x dependencies you need to:

$ go get -v

Configuration

To run the Golang proxy locally the following represents a typical sequence flow. /api/1.2 will proxy through to Mojo Perl. /api/1.3 will serve the response from the Golang proxy directly and/or interact with Postgres accordingly.

/api/1.2 routes:

TO Golang Proxy (port 8443)<-->TO Mojo Perl<-->TO Database (Postgres)

/api/1.3 routes:

TO Golang Proxy (port 8443)<-->TO Database (Postgres)

cdn.conf changes

=======================================

Copy traffic_ops/app/conf/cdn.conf to $HOME/cdn.conf so you can modify it for development purposes.

$HOME/cdn.conf

       "traffic_ops_golang" : {
          "port" : "443",
       "traffic_ops_golang" : {
          "port" : "8443",

Logging

=======================================

By default /var/log/traffic_ops/error.log is configured for output, to change this modify your $HOME/cdn.conf for the following:

$HOME/cdn.conf

    "traffic_ops_golang" : {
        "..."
        "log_location_error": "stdout",
        "log_location_warning": "stdout",
        "log_location_info": "stdout",
        "log_location_debug": "stdout",
        "log_location_event": "stdout",
        ...
     }

Development

Go is a compiled language so any local changes will require you to CTRL-C the console and re-run the traffic_ops_golang Go binary locally:

go build && ./traffic_ops_golang -cfg $HOME/cdn.conf -dbcfg ../app/conf/development/database.conf

Converting Routes to Traffic Ops Golang

Traffic Ops is moving to Go! You can help!

We're in the process of migrating the Perl/Mojolicious Traffic Ops to Go. This involves converting each route, one-by-one. There are many small, simple routes, like /api/1.2/regions and api/1.2/divisions. If you want to help, you can convert some of these.

You‘ll need at least a basic understanding of Perl and Go, or be willing to learn them. You’ll also need a running Traffic Ops instance, to compare the old and new routes and make sure they're identical.

Converting an Endpoint

Perl

If you don‘t already have an endpoint in mind, open TrafficOpsRoutes.pm and browse the routes. Start with /api/ routes. We’ll be moving others, like config files, but they‘re a bit more complex. We specifically won’t be moving GUI routes (e.g. /asns), they'll go away when the new Portal is done.

After you pick a route, you‘ll need to look at the code that generates it. For example, if we look at $r->get("/api/$version/cdns")->over( authenticated => 1, not_ldap => 1 )->to( 'Cdn#index', namespace => $namespace );, we see it’s calling Cdn#index, so we look in app/lib/API/Cdn.pm at sub index.

As you can see, this is a very simple route. It queries the database CDN table, and puts the id, name, domainName, dnssecEnabled, and lastUpdated fields in an object, for every database entry, in an array.

If you go to /api/1.2/cdns in a browser, you'll see Perl is also wrapping it in a "response" object.

Go


Now we need to create the Go endpoint.

Getting a “Handle” on Routes

Open 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.

Creating a Handler

The first step is to create your handler. For an example, look at monitoringHandler in monitoring.go. Your handler arguments can be any data available to the router (the config and database, or what you can create from them). Passing the db or prepared Stmts is common. The handler function must return a RegexHandlerFunc. In general, you want to return an inline function, return func(w http.ResponseWriter, r *http.Request, p ParamMap) {....

The ResponseWriter and Request are standard Go HandlerFunc parameters. The ParamMap is a map[string]string, containing the variables from your route path.

Now, your handler just needs to load the data, format it, and write it to the ResponseWriter, like any other Go HandlerFunc.

This is the hard part, where you have to recreate the Perl response. But it‘s all standard Go programming, reading from a database, creating JSON, and writing to the http.ResponseWriter. If you’re just learning Go, look at some of the other endpoints like monitoring.go, and maybe google some Golang tutorials on SQL, JSON, and HTTP. The Go documentation is also helpful, particularly https://golang.org/pkg/database/sql/ and https://golang.org/pkg/encoding/json/.

Your handler should be in its own file, where you can create any structs and helper functions you need.

Registering the Handler

Back to routes.go, you need to add your handler to the Routes function. For example, /api/1.2/cdns would look like {1.2, http.MethodGet, "cdns", wrapHeaders(wrapAuth(cdnsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, CdnsPrivLevel))},.

The only thing we haven‘t talked about are those wrap functions. They each take a RegexHandlerFunc and return a RegexHandlerFunc, which lets them ‘wrap’ your handler. You almost certainly need both of them; if you’re not sure, ask on the mailing list or Slack. You'll notice the wrapAuth function also takes config parameters, as well as a PrivLevel. You should create a constant in your handler file of the form EndpointPrivLevel and pass that. If your endpoint modifies data, use PrivLevelOperations, otherwise PrivLevelReadOnly.

That‘s it! Test your endpoint, read Contributing.md if you haven’t, and submit a pull request!

If you have any trouble, or suggestions for this guide, hit us up on the mailing list or Slack.