diff --git a/artifacts/grafana-kiosk-1.0.2.tar.gz b/artifacts/grafana-kiosk-1.0.2.tar.gz
new file mode 100644
index 0000000..e62e4cc
--- /dev/null
+++ b/artifacts/grafana-kiosk-1.0.2.tar.gz
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e9551c2edc310f3b42acc0a86984ff4675b2b36e44b7db9d9084a1ac3fd3b172
+size 57177816
diff --git a/artifacts/grafana-kiosk-1.0.2.zip b/artifacts/grafana-kiosk-1.0.2.zip
new file mode 100644
index 0000000..d7cf9e1
--- /dev/null
+++ b/artifacts/grafana-kiosk-1.0.2.zip
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de2c47523024f351dde8df5f685910a217dd1c75159914fde6d04d40f5f60b3a
+size 57163124
diff --git a/artifacts/grafana-kiosk.darwin b/artifacts/grafana-kiosk.darwin
new file mode 100755
index 0000000..ef87628
--- /dev/null
+++ b/artifacts/grafana-kiosk.darwin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7914a009baa4a1b8b38089e4f3aea3b1493f29ce90040c2766c0117c1c5912a1
+size 15412104
diff --git a/artifacts/grafana-kiosk.darwin.amd64 b/artifacts/grafana-kiosk.darwin.amd64
new file mode 100755
index 0000000..d6cc05d
--- /dev/null
+++ b/artifacts/grafana-kiosk.darwin.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bdab27212f630d2c4f7acf2f7923307ccad81bb565fec61f222ac1237d94e028
+size 12462984
diff --git a/artifacts/grafana-kiosk.linux.386 b/artifacts/grafana-kiosk.linux.386
new file mode 100755
index 0000000..6a28dcf
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.386
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7de00fbce129046f1e0aca5927710f74805d7fb32bbd177ade0536df9d2527a4
+size 13762961
diff --git a/artifacts/grafana-kiosk.linux.amd64 b/artifacts/grafana-kiosk.linux.amd64
new file mode 100755
index 0000000..d968aec
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5a6351d7bc523cd8ec9a646a3d5f982aa94f1527feb14bed263d55d570862890
+size 15586207
diff --git a/artifacts/grafana-kiosk.linux.arm64 b/artifacts/grafana-kiosk.linux.arm64
new file mode 100755
index 0000000..0e76b73
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.arm64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ad0688598c9c8d2fd416761c9e1bacbc2bd0764533b5f9f884f71d0fe1a5d5b
+size 14913945
diff --git a/artifacts/grafana-kiosk.linux.armv5 b/artifacts/grafana-kiosk.linux.armv5
new file mode 100755
index 0000000..f55be91
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.armv5
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60223d226d24d1a45b4484d740eddaf40831a950eba24813f0b131b3e0844afa
+size 13790051
diff --git a/artifacts/grafana-kiosk.linux.armv6 b/artifacts/grafana-kiosk.linux.armv6
new file mode 100755
index 0000000..f87de6d
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.armv6
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3fd7e091413eebe74570555faecf9b83df5f1361c221ccb42e20e42f829ee2e
+size 13726565
diff --git a/artifacts/grafana-kiosk.linux.armv7 b/artifacts/grafana-kiosk.linux.armv7
new file mode 100755
index 0000000..869e673
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.armv7
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:088d56f829123d0dae69df775ce22f7dd48c5023b5a63fa9ef8624170428b20a
+size 13726045
diff --git a/artifacts/grafana-kiosk.windows.amd64.exe b/artifacts/grafana-kiosk.windows.amd64.exe
new file mode 100755
index 0000000..b5df8ac
--- /dev/null
+++ b/artifacts/grafana-kiosk.windows.amd64.exe
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:083ddfa86962c9344d3085daf777f824f17ee1daf93cb69823444f6b1cf394c0
+size 12348416
diff --git a/bin/grafana-kiosk.darwin b/bin/grafana-kiosk.darwin
new file mode 100755
index 0000000..ef87628
--- /dev/null
+++ b/bin/grafana-kiosk.darwin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7914a009baa4a1b8b38089e4f3aea3b1493f29ce90040c2766c0117c1c5912a1
+size 15412104
diff --git a/bin/grafana-kiosk.darwin.amd64 b/bin/grafana-kiosk.darwin.amd64
new file mode 100755
index 0000000..d6cc05d
--- /dev/null
+++ b/bin/grafana-kiosk.darwin.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bdab27212f630d2c4f7acf2f7923307ccad81bb565fec61f222ac1237d94e028
+size 12462984
diff --git a/bin/grafana-kiosk.linux.386 b/bin/grafana-kiosk.linux.386
new file mode 100755
index 0000000..6a28dcf
--- /dev/null
+++ b/bin/grafana-kiosk.linux.386
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7de00fbce129046f1e0aca5927710f74805d7fb32bbd177ade0536df9d2527a4
+size 13762961
diff --git a/bin/grafana-kiosk.linux.amd64 b/bin/grafana-kiosk.linux.amd64
new file mode 100755
index 0000000..d968aec
--- /dev/null
+++ b/bin/grafana-kiosk.linux.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5a6351d7bc523cd8ec9a646a3d5f982aa94f1527feb14bed263d55d570862890
+size 15586207
diff --git a/bin/grafana-kiosk.linux.arm64 b/bin/grafana-kiosk.linux.arm64
new file mode 100755
index 0000000..0e76b73
--- /dev/null
+++ b/bin/grafana-kiosk.linux.arm64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ad0688598c9c8d2fd416761c9e1bacbc2bd0764533b5f9f884f71d0fe1a5d5b
+size 14913945
diff --git a/bin/grafana-kiosk.linux.armv5 b/bin/grafana-kiosk.linux.armv5
new file mode 100755
index 0000000..f55be91
--- /dev/null
+++ b/bin/grafana-kiosk.linux.armv5
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60223d226d24d1a45b4484d740eddaf40831a950eba24813f0b131b3e0844afa
+size 13790051
diff --git a/bin/grafana-kiosk.linux.armv6 b/bin/grafana-kiosk.linux.armv6
new file mode 100755
index 0000000..f87de6d
--- /dev/null
+++ b/bin/grafana-kiosk.linux.armv6
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3fd7e091413eebe74570555faecf9b83df5f1361c221ccb42e20e42f829ee2e
+size 13726565
diff --git a/bin/grafana-kiosk.linux.armv7 b/bin/grafana-kiosk.linux.armv7
new file mode 100755
index 0000000..869e673
--- /dev/null
+++ b/bin/grafana-kiosk.linux.armv7
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:088d56f829123d0dae69df775ce22f7dd48c5023b5a63fa9ef8624170428b20a
+size 13726045
diff --git a/bin/grafana-kiosk.windows.amd64.exe b/bin/grafana-kiosk.windows.amd64.exe
new file mode 100755
index 0000000..b5df8ac
--- /dev/null
+++ b/bin/grafana-kiosk.windows.amd64.exe
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:083ddfa86962c9344d3085daf777f824f17ee1daf93cb69823444f6b1cf394c0
+size 12348416
diff --git a/coverage/cover.out b/coverage/cover.out
new file mode 100644
index 0000000..4166e9d
--- /dev/null
+++ b/coverage/cover.out
@@ -0,0 +1,35 @@
+mode: set
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:10.24,31.2 18 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:33.78,44.16 7 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.2,48.16 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:44.16,47.3 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.16,51.17 3 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:51.17,53.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:32.40,49.19 15 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:56.2,57.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:60.2,60.10 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:49.19,54.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:57.16,59.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:63.23,66.22 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:71.2,74.25 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:81.2,81.43 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:66.22,70.3 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:74.25,80.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:84.33,97.2 10 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:99.13,104.27 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:130.2,132.26 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:136.2,137.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:140.2,142.29 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:147.2,150.32 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:104.27,106.68 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:106.68,109.4 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:109.9,111.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:112.8,115.48 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:118.3,128.31 10 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:115.48,117.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:132.26,134.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:137.16,138.13 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:142.29,144.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:151.15,153.32 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:154.14,156.31 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:157.10,159.36 2 0
diff --git a/coverage/coverage.html b/coverage/coverage.html
new file mode 100644
index 0000000..2defa18
--- /dev/null
+++ b/coverage/coverage.html
@@ -0,0 +1,635 @@
+
+
+
+
+
+
+
package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net/url"
+ "os"
+
+ "github.com/ilyakaznacheev/cleanenv"
+
+ "github.com/grafana/grafana-kiosk/pkg/initialize"
+ "github.com/grafana/grafana-kiosk/pkg/kiosk"
+)
+
+// Args command-line parameters
+type Args struct {
+ AutoFit bool
+ IgnoreCertificateErrors bool
+ IsPlayList bool
+ LXDEEnabled bool
+ LXDEHome string
+ ConfigPath string
+ Mode string
+ LoginMethod string
+ URL string
+ Username string
+ Password string
+}
+
+// ProcessArgs processes and handles CLI arguments
+func ProcessArgs(cfg interface{}) Args {
+ var a Args
+
+ f := flag.NewFlagSet("grafana-kiosk", flag.ContinueOnError)
+ f.StringVar(&a.ConfigPath, "c", "", "Path to configuration file (config.yaml)")
+ f.StringVar(&a.LoginMethod, "login-method", "anon", "[anon|local|gcom]")
+ f.StringVar(&a.Username, "username", "guest", "username")
+ f.StringVar(&a.Password, "password", "guest", "password")
+ f.StringVar(&a.Mode, "kiosk-mode", "full", "Kiosk Display Mode [full|tv|disabled]\nfull = No TOPNAV and No SIDEBAR\ntv = No SIDEBAR\ndisabled = omit option\n")
+ f.StringVar(&a.URL, "URL", "https://play.grafana.org", "URL to Grafana server")
+ f.BoolVar(&a.IsPlayList, "playlists", false, "URL is a playlist")
+ f.BoolVar(&a.AutoFit, "autofit", true, "Fit panels to screen")
+ f.BoolVar(&a.LXDEEnabled, "lxde", false, "Initialize LXDE for kiosk mode")
+ f.StringVar(&a.LXDEHome, "lxde-home", "/home/pi", "Path to home directory of LXDE user running X Server")
+ f.BoolVar(&a.IgnoreCertificateErrors, "ignore-certificate-errors", false, "Ignore SSL/TLS certificate error")
+
+ fu := f.Usage
+ f.Usage = func() {
+ fu()
+ envHelp, _ := cleanenv.GetDescription(cfg, nil)
+ fmt.Fprintln(f.Output())
+ fmt.Fprintln(f.Output(), envHelp)
+ }
+
+ err := f.Parse(os.Args[1:])
+ if err != nil {
+ os.Exit(-1)
+ }
+ return a
+}
+
+func setEnvironment() {
+ // for linux/X display must be set
+ var displayEnv = os.Getenv("DISPLAY")
+ if displayEnv == "" {
+ log.Println("DISPLAY not set, autosetting to :0.0")
+ os.Setenv("DISPLAY", ":0.0")
+ displayEnv = os.Getenv("DISPLAY")
+ }
+ log.Println("DISPLAY=", displayEnv)
+
+ var xAuthorityEnv = os.Getenv("XAUTHORITY")
+ if xAuthorityEnv == "" {
+ log.Println("XAUTHORITY not set, autosetting")
+ // use HOME of current user
+ var homeEnv = os.Getenv("HOME")
+ os.Setenv("XAUTHORITY", homeEnv+"/.Xauthority")
+ xAuthorityEnv = os.Getenv("XAUTHORITY")
+ }
+ log.Println("XAUTHORITY=", xAuthorityEnv)
+}
+
+func summary(cfg *kiosk.Config) {
+ // general
+ log.Println("AutoFit:", cfg.General.AutoFit)
+ log.Println("LXDEEnabled:", cfg.General.LXDEEnabled)
+ log.Println("LXDEHome:", cfg.General.LXDEHome)
+ log.Println("Mode:", cfg.General.Mode)
+ // target
+ log.Println("URL:", cfg.Target.URL)
+ log.Println("LoginMethod:", cfg.Target.LoginMethod)
+ log.Println("Username:", cfg.Target.Username)
+ log.Println("Password:", "*redacted*")
+ log.Println("IgnoreCertificateErrors:", cfg.Target.IgnoreCertificateErrors)
+ log.Println("IsPlayList:", cfg.Target.IsPlayList)
+}
+
+func main() {
+ var cfg kiosk.Config
+ // override
+ args := ProcessArgs(&cfg)
+ // check if config specified
+ if args.ConfigPath != "" {
+ // read configuration from the file and then override with environment variables
+ if err := cleanenv.ReadConfig(args.ConfigPath, &cfg); err != nil {
+ log.Println("Error reading config file", err)
+ os.Exit(-1)
+ } else {
+ log.Println("Using config from", args.ConfigPath)
+ }
+ } else {
+ log.Println("No config specified, using environment and args")
+ // no config, use environment and args
+ if err := cleanenv.ReadEnv(&cfg); err != nil {
+ log.Println("Error reading config from environment", err)
+ }
+ cfg.Target.URL = args.URL
+ cfg.Target.LoginMethod = args.LoginMethod
+ cfg.Target.Username = args.Username
+ cfg.Target.Password = args.Password
+ cfg.Target.IgnoreCertificateErrors = args.IgnoreCertificateErrors
+ cfg.Target.IsPlayList = args.IsPlayList
+ //
+ cfg.General.AutoFit = args.AutoFit
+ cfg.General.LXDEEnabled = args.LXDEEnabled
+ cfg.General.LXDEHome = args.LXDEHome
+ cfg.General.Mode = args.Mode
+ }
+ summary(&cfg)
+ // make sure the url has content
+ if cfg.Target.URL == "" {
+ os.Exit(1)
+ }
+ // validate url
+ _, err := url.ParseRequestURI(cfg.Target.URL)
+ if err != nil {
+ panic(err)
+ }
+ summary(&cfg)
+
+ if cfg.General.LXDEEnabled {
+ initialize.LXDE(cfg.General.LXDEHome)
+ }
+
+ // for linux/X display must be set
+ setEnvironment()
+ log.Println("method ", cfg.Target.LoginMethod)
+
+ switch cfg.Target.LoginMethod {
+ case "local":
+ log.Printf("Launching local login kiosk")
+ kiosk.GrafanaKioskLocal(&cfg)
+ case "gcom":
+ log.Printf("Launching GCOM login kiosk")
+ kiosk.GrafanaKioskGCOM(&cfg)
+ default:
+ log.Printf("Launching ANON login kiosk")
+ kiosk.GrafanaKioskAnonymous(&cfg)
+ }
+}
+
+
+
package initialize
+
+import (
+ "log"
+ "os"
+ "os/exec"
+)
+
+// LXDE runs shell commands to setup LXDE for kiosk mode
+func LXDE(path string) {
+ var command = "/usr/bin/lxpanel"
+
+ args := []string{"--profile", "LXDE"}
+ runCommand(path, command, args, true)
+ command = "/usr/bin/pcmanfm"
+ args = []string{"--desktop", "--profile", "LXDE"}
+ runCommand(path, command, args, true)
+ command = "/usr/bin/xset"
+ runCommand(path, command, args, true)
+ args = []string{"s", "off"}
+ runCommand(path, command, args, true)
+ args = []string{"-dpms"}
+ runCommand(path, command, args, true)
+ args = []string{"s", "noblank"}
+ runCommand(path, command, args, true)
+ command = "/usr/bin/unclutter"
+ displayEnv := os.Getenv("DISPLAY")
+ args = []string{"-display", displayEnv, "-idle", "5"}
+
+ go runCommand(path, command, args, true)
+}
+
+func runCommand(path string, command string, args []string, waitForEnd bool) {
+ // check if command exists
+ log.Printf("path: %v", path)
+ log.Printf("command: %v", command)
+ log.Printf("arg0: %v", args[0])
+ cmd := exec.Command(command, args...)
+ cmd.Env = append(os.Environ(),
+ "DISPLAY=:0.0",
+ "XAUTHORITY="+path+"/.Xauthority",
+ )
+ err := cmd.Start()
+ if err != nil {
+ //log.Printf(err)
+ log.Printf("Error in output, ignoring...")
+ }
+ if waitForEnd {
+ log.Printf("Waiting for command to finish...")
+ err = cmd.Wait()
+ if err != nil {
+ log.Printf("Command finished with error: %v", err)
+ }
+ }
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+)
+
+// GrafanaKioskAnonymous creates a chrome-based kiosk using a local grafana-server account
+func GrafanaKioskAnonymous(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ // chromedp.DisableGPU, // needed?
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.Flag("ignore-certificate-errors", cfg.Target.IgnoreCertificateErrors),
+ chromedp.Flag("test-type", cfg.Target.IgnoreCertificateErrors),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, consoleAPICall|targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+ /*
+ Launch chrome and look for main-view element
+ */
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.WaitVisible(`//div[@class="main-view"]`, chromedp.BySearch),
+ // wait forever (for now)
+ chromedp.WaitVisible("notinputPassword", chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+ log.Println("Sleep before exit...")
+ // wait here for the process to exit
+ time.Sleep(2000 * time.Millisecond)
+ log.Println("Exit...")
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+ "github.com/chromedp/chromedp/kb"
+)
+
+// GrafanaKioskGCOM creates a chrome-based kiosk using a grafana.com authenticated account
+func GrafanaKioskGCOM(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ // chromedp.DisableGPU, // needed?
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+
+ /*
+ Launch chrome, click the grafana.com button, fill out login form and submit
+ */
+ // XPATH of grafana.com login button = //*[@href="login/grafana_com"]/i
+
+ // Click the grafana_com login button
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.WaitVisible(`//*[@href="login/grafana_com"]/i`, chromedp.BySearch),
+ chromedp.Click(`//*[@href="login/grafana_com"]/..`, chromedp.BySearch),
+ ); err != nil {
+ panic(err)
+ }
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+ // Fill out grafana_com login page
+ if err := chromedp.Run(taskCtx,
+ chromedp.WaitVisible(`//input[@name="login"]`, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="login"]`, cfg.Target.Username, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="password"]`, cfg.Target.Password+kb.Enter, chromedp.BySearch),
+ chromedp.WaitVisible(`notinputPassword`, chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "log"
+
+ "github.com/chromedp/cdproto/inspector"
+ "github.com/chromedp/cdproto/runtime"
+ "github.com/chromedp/chromedp"
+)
+
+type chromeEvents int
+
+const (
+ consoleAPICall chromeEvents = 1 << iota
+ targetCrashed
+)
+
+func listenChromeEvents(taskCtx context.Context, events chromeEvents) {
+ chromedp.ListenTarget(taskCtx, func(ev interface{}) {
+ switch ev := ev.(type) {
+ case *runtime.EventConsoleAPICalled:
+ if events&consoleAPICall != 0 {
+ log.Printf("console.%s call:\n", ev.Type)
+ for _, arg := range ev.Args {
+ log.Printf(" %s - %s\n", arg.Type, arg.Value)
+ }
+ }
+ case *inspector.EventTargetCrashed:
+ if events&targetCrashed != 0 {
+ log.Printf("target crashed, reload...")
+ go func() {
+ _ = chromedp.Run(taskCtx, chromedp.Reload())
+ }()
+ }
+ }
+ })
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+ "github.com/chromedp/chromedp/kb"
+)
+
+// GrafanaKioskLocal creates a chrome-based kiosk using a local grafana-server account
+func GrafanaKioskLocal(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ // chromedp.DisableGPU, // needed?
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.Flag("ignore-certificate-errors", cfg.Target.IgnoreCertificateErrors),
+ chromedp.Flag("test-type", cfg.Target.IgnoreCertificateErrors),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+ /*
+ Launch chrome and login with local user account
+
+ name=user, type=text
+ id=inputPassword, type=password, name=password
+ */
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.WaitVisible(`//input[@name="user"]`, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="user"]`, cfg.Target.Username, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="password"]`, cfg.Target.Password+kb.Enter, chromedp.BySearch),
+ chromedp.WaitVisible(`notinputPassword`, chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+}
+
+
+
package kiosk
+
+import (
+ "log"
+ "net/url"
+)
+
+// GenerateURL constructs URL with appropriate parameters for kiosk mode
+func GenerateURL(anURL string, kioskMode string, autoFit bool, isPlayList bool) string {
+ u, _ := url.ParseRequestURI(anURL)
+ q, _ := url.ParseQuery(u.RawQuery)
+
+ switch kioskMode {
+ case "tv": // TV
+ q.Set("kiosk", "tv") // no sidebar, topnav without buttons
+ log.Printf("KioskMode: TV")
+ case "full": // FULLSCREEN
+ q.Set("kiosk", "1") // sidebar and topnav always shown
+ log.Printf("KioskMode: Fullscreen")
+ case "disabled": // FULLSCREEN
+ log.Printf("KioskMode: Disabled")
+ default: // disabled
+ q.Set("kiosk", "1") // sidebar and topnav always shown
+ log.Printf("KioskMode: Fullscreen")
+ }
+ // a playlist should also go inactive immediately
+ if isPlayList {
+ q.Set("inactive", "1")
+ }
+ u.RawQuery = q.Encode()
+ if autoFit {
+ u.RawQuery += "&autofitpanels"
+ }
+ return u.String()
+}
+
+
+
+
+
+
diff --git a/coverage/coverage.out b/coverage/coverage.out
new file mode 100644
index 0000000..009afb1
--- /dev/null
+++ b/coverage/coverage.out
@@ -0,0 +1,147 @@
+mode: set
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:19.71,20.54 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:20.54,21.26 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:22.39,23.34 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:29.38,30.33 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:23.34,25.33 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:25.33,27.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:30.33,32.15 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:32.15,34.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:15.37,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:20.2,48.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:52.2,69.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:48.46,49.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:69.16,70.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:9.88,13.19 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.2,27.16 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:30.2,31.13 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:34.2,34.19 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:14.12,16.30 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:17.14,19.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:20.18,21.36 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:22.10,24.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.16,29.3 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:31.13,33.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:32.40,49.19 15 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:56.2,57.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:60.2,60.10 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:49.19,54.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:57.16,59.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:63.23,66.22 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:71.2,74.25 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:81.2,81.43 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:66.22,70.3 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:74.25,80.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:84.33,97.2 10 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:99.13,104.27 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:130.2,132.26 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:136.2,137.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:140.2,142.29 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:147.2,150.32 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:104.27,106.68 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:106.68,109.4 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:109.9,111.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:112.8,115.48 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:118.3,128.31 10 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:115.48,117.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:132.26,134.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:137.16,138.13 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:142.29,144.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:151.15,153.32 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:154.14,156.31 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:157.10,159.36 2 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:10.24,31.2 18 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:33.78,44.16 7 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.2,48.16 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:44.16,47.3 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.16,51.17 3 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:51.17,53.4 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:14.41,16.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:19.2,47.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:52.2,64.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:67.2,70.24 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:16.16,17.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:47.46,48.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:64.16,65.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:15.36,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:20.2,46.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:50.2,63.16 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:67.2,74.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:46.46,47.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:63.16,64.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:74.16,75.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:15.36,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:20.2,46.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:50.2,63.16 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:67.2,74.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:46.46,47.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:63.16,64.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:74.16,75.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:19.71,20.54 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:20.54,21.26 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:22.39,23.34 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:29.38,30.33 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:23.34,25.33 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:25.33,27.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:30.33,32.15 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:32.15,34.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:15.37,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:20.2,48.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:52.2,69.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:48.46,49.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:69.16,70.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:9.88,13.19 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.2,27.16 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:30.2,31.13 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:34.2,34.19 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:14.12,16.30 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:17.14,19.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:20.18,21.36 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:22.10,24.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.16,29.3 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:31.13,33.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:32.40,49.19 15 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:56.2,57.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:60.2,60.10 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:49.19,54.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:57.16,59.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:63.23,66.22 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:71.2,74.25 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:81.2,81.43 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:66.22,70.3 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:74.25,80.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:84.33,97.2 10 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:99.13,104.27 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:130.2,132.26 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:136.2,137.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:140.2,142.29 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:147.2,150.32 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:104.27,106.68 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:106.68,109.4 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:109.9,111.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:112.8,115.48 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:118.3,128.31 10 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:115.48,117.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:132.26,134.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:137.16,138.13 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:142.29,144.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:151.15,153.32 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:154.14,156.31 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:157.10,159.36 2 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:10.24,31.2 18 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:33.78,44.16 7 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.2,48.16 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:44.16,47.3 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.16,51.17 3 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:51.17,53.4 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:14.41,16.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:19.2,47.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:52.2,64.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:67.2,70.24 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:16.16,17.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:47.46,48.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:64.16,65.13 1 0