github.com/kiali/kiali@v1.84.0/kiali.go (about) 1 // Kiali 2 // 3 // # Kiali Project, The Console for Istio Service Mesh 4 // 5 // NOTE! The Kiali API is not for public use and is not supported for any use outside of the Kiali UI itself. 6 // The API can and will change from version to version with no guarantee of backwards compatibility. 7 // 8 // To generate this API document: 9 // ``` 10 // 11 // > alias swagger='docker run --rm -it --user $(id -u):$(id -g) -e GOCACHE=/tmp -e GOPATH=$(go env GOPATH):/go -v $HOME:$HOME -w $(pwd) quay.io/goswagger/swagger' 12 // > swagger generate spec -o ./swagger.json 13 // > swagger generate markdown --quiet --spec ./swagger.json --output ./kiali_internal_api.md 14 // 15 // ``` 16 // 17 // Schemes: http, https 18 // BasePath: /api 19 // Version: _ 20 // 21 // Consumes: 22 // - application/json 23 // 24 // Produces: 25 // - application/json 26 // 27 // swagger:meta 28 package main 29 30 import ( 31 "context" 32 "flag" 33 "os" 34 "os/signal" 35 "strings" 36 "syscall" 37 38 _ "go.uber.org/automaxprocs" 39 40 "github.com/kiali/kiali/business" 41 "github.com/kiali/kiali/config" 42 "github.com/kiali/kiali/kubernetes" 43 "github.com/kiali/kiali/kubernetes/cache" 44 "github.com/kiali/kiali/log" 45 "github.com/kiali/kiali/prometheus" 46 "github.com/kiali/kiali/prometheus/internalmetrics" 47 "github.com/kiali/kiali/server" 48 "github.com/kiali/kiali/status" 49 "github.com/kiali/kiali/tracing" 50 "github.com/kiali/kiali/util" 51 ) 52 53 // Identifies the build. These are set via ldflags during the build (see Makefile). 54 var ( 55 version = "unknown" 56 commitHash = "unknown" 57 goVersion = "unknown" 58 ) 59 60 // Command line arguments 61 var ( 62 argConfigFile = flag.String("config", "", "Path to the YAML configuration file. If not specified, environment variables will be used for configuration.") 63 ) 64 65 func init() { 66 // log everything to stderr so that it can be easily gathered by logs, separate log files are problematic with containers 67 _ = flag.Set("logtostderr", "true") 68 } 69 70 func main() { 71 log.InitializeLogger() 72 util.Clock = util.RealClock{} 73 74 // process command line 75 flag.Parse() 76 validateFlags() 77 78 // log startup information 79 log.Infof("Kiali: Version: %v, Commit: %v, Go: %v", version, commitHash, goVersion) 80 log.Debugf("Kiali: Command line: [%v]", strings.Join(os.Args, " ")) 81 82 // load config file if specified, otherwise, rely on environment variables to configure us 83 if *argConfigFile != "" { 84 c, err := config.LoadFromFile(*argConfigFile) 85 if err != nil { 86 log.Fatal(err) 87 } 88 config.Set(c) 89 } else { 90 log.Infof("No configuration file specified. Will rely on environment for configuration.") 91 config.Set(config.NewConfig()) 92 } 93 94 updateConfigWithIstioInfo() 95 96 cfg := config.Get() 97 log.Tracef("Kiali Configuration:\n%s", cfg) 98 99 if err := config.Validate(*cfg); err != nil { 100 log.Fatal(err) 101 } 102 103 status.Put(status.CoreVersion, version) 104 status.Put(status.CoreCommitHash, commitHash) 105 status.Put(status.ContainerVersion, determineContainerVersion(version)) 106 107 // prepare our internal metrics so Prometheus can scrape them 108 internalmetrics.RegisterInternalMetrics() 109 110 // Create the business package dependencies. 111 clientFactory, err := kubernetes.GetClientFactory() 112 if err != nil { 113 log.Fatalf("Failed to create client factory. Err: %s", err) 114 } 115 116 log.Info("Initializing Kiali Cache") 117 cache, err := cache.NewKialiCache(clientFactory, *cfg) 118 if err != nil { 119 log.Fatalf("Error initializing Kiali Cache. Details: %s", err) 120 } 121 defer cache.Stop() 122 123 namespaceService := business.NewNamespaceService(clientFactory.GetSAClients(), clientFactory.GetSAClients(), cache, cfg) 124 meshService := business.NewMeshService(clientFactory.GetSAClients(), cache, namespaceService, *cfg) 125 cpm := business.NewControlPlaneMonitor(cache, clientFactory, *cfg, &meshService) 126 127 // This context is used for polling and for creating some high level clients like tracing. 128 ctx, cancel := context.WithCancel(context.Background()) 129 defer cancel() 130 131 if cfg.ExternalServices.Istio.IstioAPIEnabled { 132 cpm.PollIstiodForProxyStatus(ctx) 133 } 134 135 // Create shared prometheus client shared by all prometheus requests in the business layer. 136 prom, err := prometheus.NewClient() 137 if err != nil { 138 log.Fatalf("Error creating Prometheus client: %s", err) 139 } 140 141 // Create shared tracing client shared by all tracing requests in the business layer. 142 // Because tracing is not an essential component, we don't want to block startup 143 // of the server if the tracing client fails to initialize. tracing.NewClient will 144 // continue to retry until the client is initialized or the context is cancelled. 145 // Passing in a loader function allows the tracing client to be used once it is 146 // finally initialized. 147 var tracingClient tracing.ClientInterface 148 tracingLoader := func() tracing.ClientInterface { 149 return tracingClient 150 } 151 if cfg.ExternalServices.Tracing.Enabled { 152 go func() { 153 client, err := tracing.NewClient(ctx, cfg, clientFactory.GetSAHomeClusterClient().GetToken()) 154 if err != nil { 155 log.Fatalf("Error creating tracing client: %s", err) 156 return 157 } 158 tracingClient = client 159 }() 160 } else { 161 log.Debug("Tracing is disabled") 162 } 163 164 // Start listening to requests 165 server, err := server.NewServer(cpm, clientFactory, cache, cfg, prom, tracingLoader) 166 if err != nil { 167 log.Fatal(err) 168 } 169 server.Start() 170 171 // wait forever, or at least until we are told to exit 172 waitForTermination() 173 174 // Shutdown internal components 175 log.Info("Shutting down internal components") 176 server.Stop() 177 } 178 179 func waitForTermination() { 180 // Channel that is notified when we are done and should exit 181 // TODO: may want to make this a package variable - other things might want to tell us to exit 182 doneChan := make(chan bool) 183 184 signalChan := make(chan os.Signal, 1) 185 signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) 186 go func() { 187 for range signalChan { 188 log.Info("Termination Signal Received") 189 doneChan <- true 190 } 191 }() 192 193 <-doneChan 194 } 195 196 func validateFlags() { 197 if *argConfigFile != "" { 198 if _, err := os.Stat(*argConfigFile); err != nil { 199 if os.IsNotExist(err) { 200 log.Debugf("Configuration file [%v] does not exist.", *argConfigFile) 201 } 202 } 203 } 204 } 205 206 // determineContainerVersion will return the version of the image container. 207 // It does this by looking at an ENV defined in the Dockerfile when the image is built. 208 // If the ENV is not defined, the version is assumed the same as the given default value. 209 func determineContainerVersion(defaultVersion string) string { 210 v := os.Getenv("KIALI_CONTAINER_VERSION") 211 if v == "" { 212 return defaultVersion 213 } 214 return v 215 } 216 217 // This is used to update the config with information about istio that 218 // comes from the environment such as the cluster name. 219 func updateConfigWithIstioInfo() { 220 conf := *config.Get() 221 222 homeCluster := conf.KubernetesConfig.ClusterName 223 if homeCluster != "" { 224 // If the cluster name is already set, we don't need to do anything 225 return 226 } 227 228 err := func() error { 229 log.Debug("Cluster name is not set. Attempting to auto-detect the cluster name from the home cluster environment.") 230 ctx, cancel := context.WithCancel(context.Background()) 231 defer cancel() 232 233 // Need to create a temporary client factory here so that we can create a client 234 // to auto-detect the istio cluster name from the environment. There's a bit of a 235 // chicken and egg problem with the client factory because the client factory 236 // uses the cluster id to keep track of all the clients. But in order to create 237 // a client to get the cluster id from the environment, you need to create a client factory. 238 // To get around that we create a temporary client factory here and then set the kiali 239 // config cluster name. We then create the global client factory later in the business 240 // package and that global client factory has the cluster id set properly. 241 cf, err := kubernetes.NewClientFactory(ctx, conf) 242 if err != nil { 243 return err 244 } 245 246 // Try to auto-detect the cluster name 247 homeCluster, _, err = kubernetes.ClusterInfoFromIstiod(conf, cf.GetSAHomeClusterClient()) 248 if err != nil { 249 return err 250 } 251 252 return nil 253 }() 254 if err != nil { 255 log.Warningf("Cannot resolve local cluster name. Err: %s. Falling back to [%s]", err, config.DefaultClusterID) 256 homeCluster = config.DefaultClusterID 257 } 258 259 log.Debugf("Auto-detected the istio cluster name to be [%s]. Updating the kiali config", homeCluster) 260 conf.KubernetesConfig.ClusterName = homeCluster 261 config.Set(&conf) 262 }