| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | package renderer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-07-31 22:04:04 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-05-04 15:07:45 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-02-16 17:09:26 +08:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 19:36:21 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 17:37:13 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/log" | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/middleware" | 
					
						
							| 
									
										
										
										
											2015-02-05 17:37:13 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2015-04-08 14:59:12 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/util" | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type RenderOpts struct { | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 	Path     string | 
					
						
							|  |  |  | 	Width    string | 
					
						
							|  |  |  | 	Height   string | 
					
						
							|  |  |  | 	Timeout  string | 
					
						
							|  |  |  | 	OrgId    int64 | 
					
						
							|  |  |  | 	Timezone string | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 22:04:04 +08:00
										 |  |  | var ErrTimeout = errors.New("Timeout error. You can set timeout in seconds with &timeout url parameter") | 
					
						
							| 
									
										
										
										
											2016-07-30 19:36:21 +08:00
										 |  |  | var rendererLog log.Logger = log.New("png-renderer") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-15 17:15:20 +08:00
										 |  |  | func isoTimeOffsetToPosixTz(isoOffset string) string { | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 	// invert offset
 | 
					
						
							|  |  |  | 	if strings.HasPrefix(isoOffset, "UTC+") { | 
					
						
							|  |  |  | 		return strings.Replace(isoOffset, "UTC+", "UTC-", 1) | 
					
						
							| 
									
										
										
										
											2017-01-15 17:15:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 	if strings.HasPrefix(isoOffset, "UTC-") { | 
					
						
							|  |  |  | 		return strings.Replace(isoOffset, "UTC-", "UTC+", 1) | 
					
						
							| 
									
										
										
										
											2017-01-15 17:15:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 	return isoOffset | 
					
						
							| 
									
										
										
										
											2017-01-15 17:15:20 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func appendEnviron(baseEnviron []string, name string, value string) []string { | 
					
						
							|  |  |  | 	results := make([]string, 0) | 
					
						
							|  |  |  | 	prefix := fmt.Sprintf("%s=", name) | 
					
						
							|  |  |  | 	for _, v := range baseEnviron { | 
					
						
							|  |  |  | 		if !strings.HasPrefix(v, prefix) { | 
					
						
							|  |  |  | 			results = append(results, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return append(results, fmt.Sprintf("%s=%s", name, value)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | func RenderToPng(params *RenderOpts) (string, error) { | 
					
						
							| 
									
										
										
										
											2016-09-26 17:07:36 +08:00
										 |  |  | 	rendererLog.Info("Rendering", "path", params.Path) | 
					
						
							| 
									
										
										
										
											2016-02-16 17:09:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var executable = "phantomjs" | 
					
						
							|  |  |  | 	if runtime.GOOS == "windows" { | 
					
						
							|  |  |  | 		executable = executable + ".exe" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 17:51:46 +08:00
										 |  |  | 	localDomain := "localhost" | 
					
						
							| 
									
										
										
										
											2016-11-23 22:35:43 +08:00
										 |  |  | 	if setting.HttpAddr != setting.DEFAULT_HTTP_ADDR { | 
					
						
							| 
									
										
										
										
											2016-12-05 17:51:46 +08:00
										 |  |  | 		localDomain = setting.HttpAddr | 
					
						
							| 
									
										
										
										
											2016-11-23 22:35:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 17:51:46 +08:00
										 |  |  | 	url := fmt.Sprintf("%s://%s:%s/%s", setting.Protocol, localDomain, setting.HttpPort, params.Path) | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 17:09:26 +08:00
										 |  |  | 	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable)) | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js")) | 
					
						
							| 
									
										
										
										
											2015-04-08 14:59:12 +08:00
										 |  |  | 	pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20))) | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	pngPath = pngPath + ".png" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 	renderKey := middleware.AddRenderAuthKey(params.OrgId) | 
					
						
							|  |  |  | 	defer middleware.RemoveRenderAuthKey(renderKey) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 22:04:04 +08:00
										 |  |  | 	timeout, err := strconv.Atoi(params.Timeout) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		timeout = 15 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 	cmdArgs := []string{ | 
					
						
							|  |  |  | 		"--ignore-ssl-errors=true", | 
					
						
							| 
									
										
										
										
											2016-12-06 15:37:36 +08:00
										 |  |  | 		"--web-security=false", | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 		scriptPath, | 
					
						
							| 
									
										
										
										
											2016-09-26 17:07:36 +08:00
										 |  |  | 		"url=" + url, | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 		"width=" + params.Width, | 
					
						
							|  |  |  | 		"height=" + params.Height, | 
					
						
							|  |  |  | 		"png=" + pngPath, | 
					
						
							| 
									
										
										
										
											2016-12-05 17:51:46 +08:00
										 |  |  | 		"domain=" + localDomain, | 
					
						
							| 
									
										
										
										
											2017-07-31 22:04:04 +08:00
										 |  |  | 		"timeout=" + strconv.Itoa(timeout), | 
					
						
							| 
									
										
										
										
											2016-09-23 18:29:53 +08:00
										 |  |  | 		"renderKey=" + renderKey, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd := exec.Command(binPath, cmdArgs...) | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	stdout, err := cmd.StdoutPipe() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	stderr, err := cmd.StderrPipe() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 	if params.Timezone != "" { | 
					
						
							| 
									
										
										
										
											2017-01-15 17:15:20 +08:00
										 |  |  | 		baseEnviron := os.Environ() | 
					
						
							| 
									
										
										
										
											2017-01-31 14:48:10 +08:00
										 |  |  | 		cmd.Env = appendEnviron(baseEnviron, "TZ", isoTimeOffsetToPosixTz(params.Timezone)) | 
					
						
							| 
									
										
										
										
											2017-01-15 17:15:20 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	err = cmd.Start() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go io.Copy(os.Stdout, stdout) | 
					
						
							|  |  |  | 	go io.Copy(os.Stdout, stderr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	done := make(chan error) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2017-10-25 14:14:14 +08:00
										 |  |  | 		if err := cmd.Wait(); err != nil { | 
					
						
							|  |  |  | 			rendererLog.Error("failed to render an image", "error", err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 		close(done) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	select { | 
					
						
							| 
									
										
										
										
											2016-03-10 23:02:10 +08:00
										 |  |  | 	case <-time.After(time.Duration(timeout) * time.Second): | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 		if err := cmd.Process.Kill(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-30 19:36:21 +08:00
										 |  |  | 			rendererLog.Error("failed to kill", "error", err) | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-07-31 22:04:04 +08:00
										 |  |  | 		return "", ErrTimeout | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	case <-done: | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 19:36:21 +08:00
										 |  |  | 	rendererLog.Debug("Image rendered", "path", pngPath) | 
					
						
							| 
									
										
										
										
											2014-10-07 03:31:54 +08:00
										 |  |  | 	return pngPath, nil | 
					
						
							|  |  |  | } |