in main.go [81:206]
func Main() error {
frontendBuildDir := filepath.FromSlash(*frontendDir)
indexFilePath := filepath.Join(frontendBuildDir, "index.html")
faviconFilePath := filepath.Join(frontendBuildDir, "favicon.ico")
staticDirPath := filepath.Join(frontendBuildDir, "static")
imagesDirPath := filepath.Join(frontendBuildDir, "images")
var backendURLs []*url.URL
if *backendAddrs == "" {
*backendAddrs = os.Getenv("OPBEANS_SERVICES")
}
if *backendAddrs != "" {
for _, field := range strings.Split(*backendAddrs, ",") {
field = strings.TrimSpace(field)
if u, err := url.Parse(field); err == nil && u.Scheme != "" {
backendURLs = append(backendURLs, u)
continue
}
// Not an absolute URL, so should be a host or host/port pair.
hostport := field
if _, _, err := net.SplitHostPort(hostport); err != nil {
// A bare host was specified; assume the same port
// that we're listening on.
_, port, err := net.SplitHostPort(*listenAddr)
if err != nil {
port = "3000"
}
hostport = net.JoinHostPort(hostport, port)
}
backendURLs = append(backendURLs, &url.URL{Scheme: "http", Host: hostport})
}
}
// Read index.html, replace <head> with <head><script>...
// that injects the dynamic page load properties for RUM.
indexFileBytes, err := ioutil.ReadFile(indexFilePath)
if err != nil {
return err
}
indexFileContent := strings.Replace(string(indexFileBytes), "<head>", `<head>
<script type="text/javascript">
window.rumConfig = {
pageLoadTraceId: {{.TraceContext.Trace}},
pageLoadSpanId: {{.EnsureParent}},
pageLoadSampled: {{.Sampled}},
}
</script>`, 1)
indexTemplate, err := template.New(indexTemplateName).Parse(indexFileContent)
if err != nil {
return err
}
db, err := newDatabase()
if err != nil {
return err
}
defer db.Close()
cacheStore, err := newCache()
if err != nil {
return err
}
r := gin.New()
r.Use(cache.Cache(&cacheStore))
r.Use(apmgin.Middleware(r))
r.Use(logrusMiddleware)
pprof.Register(r)
r.Static("/static", staticDirPath)
r.Static("/images", imagesDirPath)
r.StaticFile("/favicon.ico", faviconFilePath)
r.SetHTMLTemplate(indexTemplate)
r.GET("/", handleIndex)
r.GET("/oopsie", handleOopsie)
r.GET("/rum-config.js", handleRUMConfig)
r.Use(func(c *gin.Context) {
// Paths used by the frontend for state.
for _, prefix := range []string{
"/dashboard",
"/products",
"/customers",
"/orders",
} {
if strings.HasPrefix(c.Request.URL.Path, prefix) {
tx := apm.TransactionFromContext(c.Request.Context())
if tx != nil {
tx.Name = c.Request.Method + " " + prefix
}
handleIndex(c)
return
}
}
c.Next()
})
// Create API routes. We install middleware for /api which probabilistically
// proxies these requests to another opbeans service to demonstrate distributed
// tracing, and test agent compatibility.
proxyProbability := 0.5
if value := os.Getenv("OPBEANS_DT_PROBABILITY"); value != "" {
f, err := strconv.ParseFloat(value, 64)
if err != nil {
return errors.Wrapf(err, "failed to parse OPBEANS_DT_PROBABILITY")
}
if f < 0.0 || f > 1.0 {
return errors.Errorf("invalid OPBEANS_DT_PROBABILITY value %s: out of range [0,1.0]", value)
}
proxyProbability = f
}
rand.Seed(time.Now().UnixNano())
maybeProxy := func(c *gin.Context) {
if len(backendURLs) > 0 && rand.Float64() < proxyProbability {
u := backendURLs[rand.Intn(len(backendURLs))]
logrus.WithFields(apmlogrus.TraceContext(c.Request.Context())).Infof("proxying API request to %s", u)
httputil.NewSingleHostReverseProxy(u).ServeHTTP(c.Writer, c.Request)
c.Abort()
return
}
c.Next()
}
apiGroup := r.Group("/api", maybeProxy)
addAPIHandlers(apiGroup, db)
return r.Run(*listenAddr)
}