1 package main
2
3 import (
4 "context"
5 "flag"
6 "net"
7 "os"
8 "os/signal"
9 "regexp"
10 "syscall"
11 "time"
12
13 "github.com/linkerd/linkerd2/pkg/config"
14
15 "github.com/linkerd/linkerd2/pkg/admin"
16 "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
17 "github.com/linkerd/linkerd2/pkg/flags"
18 "github.com/linkerd/linkerd2/pkg/healthcheck"
19 "github.com/linkerd/linkerd2/pkg/k8s"
20 "github.com/linkerd/linkerd2/pkg/trace"
21 "github.com/linkerd/linkerd2/viz/metrics-api/client"
22 "github.com/linkerd/linkerd2/web/srv"
23 log "github.com/sirupsen/logrus"
24 )
25
26 func main() {
27 cmd := flag.NewFlagSet("public-api", flag.ExitOnError)
28
29 addr := cmd.String("addr", ":8084", "address to serve on")
30 metricsAddr := cmd.String("metrics-addr", ":9994", "address to serve scrapable metrics on")
31 vizAPIAddr := cmd.String("linkerd-metrics-api-addr", "127.0.0.1:8085", "address of the linkerd-metrics-api service")
32 grafanaAddr := cmd.String("grafana-addr", "", "address of the linkerd-grafana service")
33 grafanaExternalAddr := cmd.String("grafana-external-addr", "", "address of the external grafana service")
34 grafanaPrefix := cmd.String("grafana-prefix", "", "prefix for Grafana dashboard UID's")
35 jaegerAddr := cmd.String("jaeger-addr", "", "address of the jaeger service")
36 templateDir := cmd.String("template-dir", "templates", "directory to search for template files")
37 staticDir := cmd.String("static-dir", "app/dist", "directory to search for static files")
38 reload := cmd.Bool("reload", true, "reloading set to true or false")
39 controllerNamespace := cmd.String("controller-namespace", "linkerd", "namespace in which Linkerd is installed")
40 enforcedHost := cmd.String("enforced-host", "", "regexp describing the allowed values for the Host header; protects from DNS-rebinding attacks")
41 kubeConfigPath := cmd.String("kubeconfig", "", "path to kube config")
42 clusterDomain := cmd.String("cluster-domain", "", "kubernetes cluster domain")
43 enablePprof := cmd.Bool("enable-pprof", false, "Enable pprof endpoints on the admin server")
44
45 traceCollector := flags.AddTraceFlags(cmd)
46
47 flags.ConfigureAndParse(cmd, os.Args[1:])
48
49 ready := false
50 adminServer := admin.NewServer(*metricsAddr, *enablePprof, &ready)
51
52 go func() {
53 log.Infof("starting admin server on %s", *metricsAddr)
54 if err := adminServer.ListenAndServe(); err != nil {
55 log.Errorf("failed to start web admin server: %s", err)
56 }
57 }()
58
59 ctx := context.Background()
60
61 _, _, err := net.SplitHostPort(*vizAPIAddr)
62 if err != nil {
63 log.Fatalf("failed to parse metrics API server address: %s", *vizAPIAddr)
64 }
65 client, err := client.NewInternalClient(*vizAPIAddr)
66 if err != nil {
67 log.Fatalf("failed to construct client for viz API server URL %s", *vizAPIAddr)
68 }
69
70 if *clusterDomain == "" {
71 *clusterDomain = "cluster.local"
72 log.Warnf("expected cluster domain through args (falling back to %s)", *clusterDomain)
73 }
74
75 k8sAPI, err := k8s.NewAPI(*kubeConfigPath, "", "", []string{}, 0)
76 if err != nil {
77 log.Fatalf("failed to construct Kubernetes API client: [%s]", err)
78 }
79
80
81 checks := []healthcheck.CategoryID{
82 healthcheck.KubernetesAPIChecks,
83 healthcheck.KubernetesVersionChecks,
84 healthcheck.LinkerdConfigChecks,
85 healthcheck.LinkerdControlPlaneExistenceChecks,
86 healthcheck.LinkerdVersionChecks,
87 healthcheck.LinkerdControlPlaneVersionChecks,
88 }
89 hc := healthcheck.NewHealthChecker(checks, &healthcheck.Options{
90 ControlPlaneNamespace: *controllerNamespace,
91 KubeConfig: *kubeConfigPath,
92 })
93
94 uuid, version := getUUIDAndVersion(ctx, k8sAPI, *controllerNamespace)
95
96 stop := make(chan os.Signal, 1)
97 signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
98
99 if *traceCollector != "" {
100 if err := trace.InitializeTracing("web", *traceCollector); err != nil {
101 log.Warnf("failed to initialize tracing: %s", err)
102 }
103 }
104
105 reHost, err := regexp.Compile(*enforcedHost)
106 if err != nil {
107 log.Fatalf("invalid --enforced-host parameter: %s", err)
108 }
109
110 server := srv.NewServer(*addr, *grafanaAddr, *grafanaExternalAddr, *grafanaPrefix, *jaegerAddr, *templateDir, *staticDir, uuid, version,
111 *controllerNamespace, *clusterDomain, *reload, reHost, client, k8sAPI, hc)
112
113 go func() {
114 log.Infof("starting HTTP server on %+v", *addr)
115 if err := server.ListenAndServe(); err != nil {
116 log.Errorf("failed to start web HTTP server: %s", err)
117 }
118 }()
119
120 ready = true
121
122 <-stop
123
124 log.Infof("shutting down HTTP server on %+v", *addr)
125 ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
126 defer cancel()
127 server.Shutdown(ctx)
128 adminServer.Shutdown(ctx)
129 }
130
131 func getUUIDAndVersion(ctx context.Context, k8sAPI *k8s.KubernetesAPI, controllerNamespace string) (string, string) {
132 var uuid string
133 var version string
134
135 cm, err := config.FetchLinkerdConfigMap(ctx, k8sAPI, controllerNamespace)
136 if err != nil {
137 log.Errorf("Failed to fetch linkerd-config: %s", err)
138 } else {
139 uuid = string(cm.GetUID())
140
141 values, err := linkerd2.ValuesFromConfigMap(cm)
142 if err != nil {
143 log.Errorf("failed to load values from linkerd-config: %s", err)
144 } else {
145 version = values.LinkerdVersion
146 }
147 }
148
149 return uuid, version
150 }
151
View as plain text