From 0db55b6194ad4c17917c1af8b87bb997a6c05adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 3 Mar 2015 17:14:58 +0100 Subject: [PATCH] Work on deb and rpm packages, both seem to work now, #1476 --- Gruntfile.js | 3 +- build.go | 22 ++-- docker/rpmtest/Dockerfile | 6 + docker/rpmtest/build.sh | 10 ++ main.go | 13 ++ pkg/cmd/web.go | 24 ++++ pkg/setting/data/log/grafana.log | 2 + scripts/init.sh | 202 ++++++++++++++++++++----------- tasks/distribute_task.js | 1 - tasks/options/compress.js | 38 +----- 10 files changed, 201 insertions(+), 120 deletions(-) create mode 100644 docker/rpmtest/Dockerfile create mode 100755 docker/rpmtest/build.sh diff --git a/Gruntfile.js b/Gruntfile.js index 461edc91578..aee82ecc2a8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,7 +8,8 @@ module.exports = function (grunt) { srcDir: 'src', destDir: 'dist', tempDir: 'tmp', - docsDir: 'docs/' + docsDir: 'docs/', + arch: grunt.option('arch') || 'x86_64', }; // load plugins diff --git a/build.go b/build.go index 57c4f2add5c..9fe7d371181 100644 --- a/build.go +++ b/build.go @@ -73,11 +73,11 @@ func main() { test("./pkg/...") case "package": - clean() + //clean() //verifyGitRepoIsClean() - test("./pkg/...") - build(".", []string{}) - buildFrontend() + //test("./pkg/...") + //build(".", []string{}) + //buildFrontend() createRpmAndDeb() case "build-ui": @@ -121,24 +121,22 @@ func createRpmAndDeb() { runError("mkdir", "-p", configDir) // copy sample ini file to /etc/opt/grafana - runError("cp", "conf/sample.ini", filepath.Join(configDir, "grafana.ini")) + configFile := filepath.Join(configDir, "grafana.ini") + runError("cp", "conf/sample.ini", configFile) // copy release files runError("cp", "-a", filepath.Join(workingDir, "tmp")+"/.", versionFolder) - fmt.Printf("PackageDir: %v\n", versionFolder) - GeneratePostInstallScript(postInstallScriptPath.Name()) - fmt.Printf("script_path: %v\n", postInstallScriptPath.Name()) args := []string{ "-s", "dir", - "-t", "deb", "--description", "Grafana", "-C", packageRoot, "--vendor", "Grafana", "--url", "http://grafana.org", "--license", "Apache 2.0", "--maintainer", "contact@grafana.org", + "--config-files", filepath.Join(configRoot, "grafana.ini"), "--after-install", postInstallScriptPath.Name(), "--name", "grafana", "--version", version, @@ -146,7 +144,11 @@ func createRpmAndDeb() { ".", } - runPrint("fpm", args...) + fmt.Println("Creating debian package") + runPrint("fpm", append([]string{"-t", "deb"}, args...)...) + + fmt.Println("Creating redhat/centos package") + runPrint("fpm", append([]string{"-t", "rpm"}, args...)...) } func GeneratePostInstallScript(path string) { diff --git a/docker/rpmtest/Dockerfile b/docker/rpmtest/Dockerfile new file mode 100644 index 00000000000..132d7667af1 --- /dev/null +++ b/docker/rpmtest/Dockerfile @@ -0,0 +1,6 @@ +FROM centos:7 + +RUN yum install -y initscripts + +ADD *.rpm /tmp/ + diff --git a/docker/rpmtest/build.sh b/docker/rpmtest/build.sh new file mode 100755 index 00000000000..8c3bbf2309a --- /dev/null +++ b/docker/rpmtest/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +cp Dockerfile ../../dist +cd ../../dist + +docker build --tag "grafana/rpmtest" . + +rm Dockerfile + +docker run -i -t grafana/rpmtest /bin/bash diff --git a/main.go b/main.go index ff18e7b2bda..fbda1e99543 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "os" + "os/signal" "runtime" "strconv" @@ -27,6 +28,13 @@ func main() { setting.BuildCommit = commit setting.BuildStamp = buildstampInt64 + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + os.Exit(0) + }() + app := cli.NewApp() app.Name = "Grafana Backend" app.Usage = "grafana web" @@ -46,7 +54,12 @@ func main() { Name: "config", Usage: "path to grafana.ini config file", }, + cli.StringFlag{ + Name: "pidfile", + Usage: "path to pidfile", + }, }...) + app.Run(os.Args) log.Close() diff --git a/pkg/cmd/web.go b/pkg/cmd/web.go index 0a4424446f7..7169c2dac63 100644 --- a/pkg/cmd/web.go +++ b/pkg/cmd/web.go @@ -5,8 +5,12 @@ package cmd import ( "fmt" + "io/ioutil" "net/http" + "os" "path" + "path/filepath" + "strconv" "github.com/Unknwon/macaron" "github.com/codegangsta/cli" @@ -68,6 +72,7 @@ func mapStatic(m *macaron.Macaron, dir string, prefix string) { func runWeb(c *cli.Context) { initRuntime(c) + writePIDFile(c) social.NewOAuthService() eventpublisher.Init() @@ -92,3 +97,22 @@ func runWeb(c *cli.Context) { log.Fatal(4, "Fail to start server: %v", err) } } + +func writePIDFile(c *cli.Context) { + path := c.GlobalString("pidfile") + if path == "" { + return + } + + // Ensure the required directory structure exists. + err := os.MkdirAll(filepath.Dir(path), 0700) + if err != nil { + log.Fatal(3, "Failed to verify pid directory", err) + } + + // Retrieve the PID and write it. + pid := strconv.Itoa(os.Getpid()) + if err := ioutil.WriteFile(path, []byte(pid), 0644); err != nil { + log.Fatal(3, "Failed to write pidfile", err) + } +} diff --git a/pkg/setting/data/log/grafana.log b/pkg/setting/data/log/grafana.log index 448df51d36b..0d93706fc9c 100644 --- a/pkg/setting/data/log/grafana.log +++ b/pkg/setting/data/log/grafana.log @@ -4,3 +4,5 @@ 2015/03/03 10:09:13 [I] Setting: ENV override found: GF_SECURITY_ADMIN_USER 2015/03/03 10:09:34 [I] Setting: ENV override found: GF_SECURITY_ADMIN_USER 2015/03/03 10:15:20 [I] Setting: ENV override found: GF_SECURITY_ADMIN_USER +2015/03/03 12:29:29 [I] Setting: ENV override found: GF_SECURITY_ADMIN_USER +2015/03/03 12:33:09 [I] Setting: ENV override found: GF_SECURITY_ADMIN_USER diff --git a/scripts/init.sh b/scripts/init.sh index 746487e0484..4ec83b7dddc 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -1,19 +1,21 @@ -#!/bin/sh +#! /usr/bin/env bash + ### BEGIN INIT INFO # Provides: grafana -# Required-Start: $local_fs $remote_fs $network -# Required-Stop: $local_fs $remote_fs $network +# Required-Start: $all +# Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Starts grafana at startup -# Description: Starts grafana at startup +# Short-Description: Start grafana at boot time ### END INIT INFO +# tested on +# 1. New lsb that define start-stop-daemon +# 3. Centos with initscripts package installed -# Documentation available at -# http://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptfunc.html -# Debian provides some extra functions though -. /lib/lsb/init-functions +if [ -r /lib/lsb/init-functions ]; then + source /lib/lsb/init-functions +fi DAEMON_NAME="grafana" DAEMON_USER="grafana" @@ -24,85 +26,143 @@ DAEMON_PID="/var/run/${DAEMON_NAME}.pid" DAEMON_NICE=0 DAEMON_LOG='/var/log/grafana/grafana.log' -[ -r "/etc/default/${DAEMON_NAME}" ] && . "/etc/default/${DAEMON_NAME}" +# If the daemon is not there, then exit. +[ -x $DAEMON_PATH ] || exit 5 + +if [ "x$STDOUT" == "x" ]; then + STDOUT=/tmp/grafana.log +fi + +function pidofproc() { + if [ $# -ne 3 ]; then + echo "Expected three arguments, e.g. $0 -p pidfile daemon-name" + fi + + pid=`pgrep -f $3` + local pidfile=`cat $2` + + if [ "x$pidfile" == "x" ]; then + return 1 + fi + + if [ "x$pid" != "x" -a "$pidfile" == "$pid" ]; then + return 0 + fi + + return 1 +} + +function killproc() { + if [ $# -ne 3 ]; then + echo "Expected three arguments, e.g. $0 -p pidfile signal" + fi + + pid=`cat $2` + + kill -s $3 $pid +} + +function log_failure_msg() { + echo "$@" "[ FAILED ]" +} + +function log_success_msg() { + echo "$@" "[ OK ]" +} do_start() { - local result + cd $DAEMON_PWD - pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null - if [ $? -eq 0 ]; then - log_warning_msg "${DAEMON_NAME} is already started" - result=0 - else - log_daemon_msg "Starting ${DAEMON_DESC}" "${DAEMON_NAME}" - touch "${DAEMON_LOG}" - chown $DAEMON_USER "${DAEMON_LOG}" - chmod u+rw "${DAEMON_LOG}" - if [ -z "${DAEMON_USER}" ]; then - start-stop-daemon --start --quiet --oknodo --background \ - --nicelevel $DAEMON_NICE \ - --chdir "${DAEMON_PWD}" \ - --pidfile "${DAEMON_PID}" --make-pidfile \ - --exec "${DAEMON_PATH}" -- $DAEMON_OPTS - result=$? - else - start-stop-daemon --start --quiet --oknodo --background \ - --nicelevel $DAEMON_NICE \ - --chdir "${DAEMON_PWD}" \ - --pidfile "${DAEMON_PID}" --make-pidfile \ - --chuid "${DAEMON_USER}" \ - --exec "${DAEMON_PATH}" -- $DAEMON_OPTS - result=$? - fi - log_end_msg $result - fi - return $result + # Checked the PID file exists and check the actual status of process + if [ -e $DAEMON_PID ]; then + pidofproc -p $DAEMON_PID $DAEMON_PATH > /dev/null 2>&1 && status="0" || status="$?" + # If the status is SUCCESS then don't need to start again. + if [ "x$status" = "x0" ]; then + log_failure_msg "$DAEMON_NAME process is running" + exit 1 # Exit + fi + fi + # Start the daemon. + log_success_msg "Starting the process" "$DAEMON_NAME" + + # Start the daemon with the help of start-stop-daemon + # Log the message appropriately + if which start-stop-daemon > /dev/null 2>&1; then + start-stop-daemon \ + --start --quiet --oknodo --background \ + --nicelevel $DAEMON_NICE \ + --chdir "${DAEMON_PWD}" \ + --pidfile "${DAEMON_PID}" --make-pidfile \ + --chuid "${DAEMON_USER}" \ + --exec "${DAEMON_PATH}" -- $DAEMON_OPTS + result=$? + else + touch ${DAEMON_PID} + chown $DAEMON_USER "${DAEMON_PID}" + #daemon --user $DAEMON_USER --pidfile $DAEMON_PID nohup $DAEMON_PATH $DAEMON_OPTS + su -s /bin/sh -c "nohup ${DAEMON_PATH} --pidfile=${DAEMON_PID} ${DAEMON_OPTS} >> $STDOUT 3>&1 &" $DAEMON_USER + fi + + log_success_msg "$DAEMON_NAME process was started" } do_stop() { - local result + local result - pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null - if [ $? -ne 0 ]; then - log_warning_msg "${DAEMON_NAME} is not started" - result=0 - else - log_daemon_msg "Stopping ${DAEMON_DESC}" "${DAEMON_NAME}" - killproc -p "${DAEMON_PID}" "${DAEMON_PATH}" - result=$? - log_end_msg $result - rm "${DAEMON_PID}" - fi - return $result + pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null + if [ $? -ne 0 ]; then + log_failure_msg "${DAEMON_NAME} is not started" + result=0 + else + log_success_msg "Stopping ${DAEMON_NAME}" + killproc -p "${DAEMON_PID}" SIGTERM + result=$? + if [ $result = 0 ]; then + log_success_msg "Stopped ${DAEMON_NAME}" + rm "${DAEMON_PID}" + fi + fi + + return $result } do_restart() { - local result - do_stop - result=$? - if [ $result = 0 ]; then - do_start - result=$? - fi - return $result + local result + do_stop + result=$? + sleep 2 + if [ $result = 0 ]; then + do_start + result=$? + fi + return $result } do_status() { - local result - status_of_proc -p "${DAEMON_PID}" "${DAEMON_PATH}" "${DAEMON_NAME}" - result=$? - return $result + if [ -e $DEAMON_PID ]; then + pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null + if [ $? -ne 0 ]; then + log_failure_msg "$DAEMON_NAME Process is not running" + exit 1 + else + log_success_msg "$DAEMON_NAME Process is running" + exit 0 + fi + else + log_failure_msg "$DAEMON_NAME Process is not running" + exit 3 + fi } do_usage() { - echo $"Usage: $0 {start | stop | restart | status}" - exit 1 + echo $"Usage: $0 {start | stop | restart | status}" + exit 1 } case "$1" in -start) do_start; exit $? ;; -stop) do_stop; exit $? ;; -restart) do_restart; exit $? ;; -status) do_status; exit $? ;; -*) do_usage; exit 1 ;; + start) do_start; exit $? ;; + stop) do_stop; exit $? ;; + restart) do_restart; exit $? ;; + status) do_status; exit $? ;; + *) do_usage; exit 1 ;; esac diff --git a/tasks/distribute_task.js b/tasks/distribute_task.js index 596583f6ef6..bde86acbb21 100644 --- a/tasks/distribute_task.js +++ b/tasks/distribute_task.js @@ -5,7 +5,6 @@ module.exports = function(grunt) { grunt.registerTask('release', [ 'build', 'build-post-process', - 'compress:zip_release', 'compress:tgz_release', ]); diff --git a/tasks/options/compress.js b/tasks/options/compress.js index 66038af6a4f..da80c992287 100644 --- a/tasks/options/compress.js +++ b/tasks/options/compress.js @@ -1,23 +1,5 @@ module.exports = function(config) { return { - zip: { - options: { - archive: '<%= destDir %>/<%= pkg.name %>-latest.zip' - }, - files : [ - { - expand: true, - cwd: '<%= tempDir %>', - src: ['**/*'], - dest: '<%= pkg.name %>/', - }, - { - expand: true, - dest: '<%= pkg.name %>/', - src: ['LICENSE.md', 'README.md', 'NOTICE.md'], - } - ] - }, tgz: { options: { archive: '<%= destDir %>/<%= pkg.name %>-latest.tar.gz' @@ -36,27 +18,9 @@ module.exports = function(config) { } ] }, - zip_release: { - options: { - archive: '<%= destDir %>/<%= pkg.name %>-<%= pkg.version %>.zip' - }, - files : [ - { - expand: true, - cwd: '<%= tempDir %>', - src: ['**/*'], - dest: '<%= pkg.name %>-<%= pkg.version %>/', - }, - { - expand: true, - src: ['LICENSE.md', 'README.md', 'NOTICE.md'], - dest: '<%= pkg.name %>-<%= pkg.version %>/', - } - ] - }, tgz_release: { options: { - archive: '<%= destDir %>/<%= pkg.name %>-<%= pkg.version %>.tar.gz' + archive: '<%= destDir %>/<%= pkg.name %>-<%= pkg.version %>.<%= arch %>.tar.gz' }, files : [ {