instrumental_tools 1.0.0 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/BUILD.md +15 -0
  3. data/CHANGELOG.md +17 -1
  4. data/CUSTOM_METRICS.md +4 -0
  5. data/INSTALL.md +12 -7
  6. data/README.md +2 -2
  7. data/Rakefile +202 -64
  8. data/TEST.md +18 -0
  9. data/bin/instrument_server +31 -15
  10. data/chef/.kitchen.yml +10 -1
  11. data/chef/instrumental_tools/attributes/default.rb +29 -2
  12. data/chef/instrumental_tools/recipes/default.rb +188 -17
  13. data/chef/instrumental_tools/templates/default/instrument_server.erb +46 -0
  14. data/chef/instrumental_tools/templates/{instrumental.yml.erb → default/instrumental.yml.erb} +6 -6
  15. data/chef/omnibus.sh +27 -0
  16. data/conf/instrumental.yml +6 -6
  17. data/examples/README.md +1 -0
  18. data/examples/redis/README.md +10 -0
  19. data/examples/redis/redis_info.sh +10 -0
  20. data/ext/Rakefile +1 -0
  21. data/ext/mkrf_conf.rb +18 -0
  22. data/instrumental_tools.gemspec +8 -2
  23. data/lib/instrumental_tools/metric_script_executor.rb +45 -6
  24. data/lib/instrumental_tools/server_controller.rb +4 -1
  25. data/lib/instrumental_tools/system_inspector.rb +3 -0
  26. data/lib/instrumental_tools/system_inspector/win32.rb +85 -0
  27. data/lib/instrumental_tools/version.rb +1 -1
  28. data/test/integration/default/serverspec/instrumental_tools_spec.rb +32 -16
  29. data/win32/Makefile +18 -0
  30. data/win32/installer.nsis.erb +242 -0
  31. data/win32/logo.ico +0 -0
  32. data/win32/src/instrumental/InstrumentServerProcess.cs +147 -0
  33. data/win32/src/instrumental/InstrumentServerProcessWorker.cs +89 -0
  34. data/win32/src/instrumental/InstrumentServerService.cs +146 -0
  35. data/win32/src/instrumental/InstrumentServerServiceInstaller.cs +60 -0
  36. metadata +36 -7
@@ -1,6 +1,6 @@
1
- ---
2
- # Replace YOUR_API_KEY with the API key for your Instrumental project and
3
- # remove the # from the beginning of the following line to have instrument_server
4
- # start sending metrics to your account.
5
-
6
- api_key: <%= @api_key %>
1
+ ---
2
+ # Replace YOUR_API_KEY with the API key for your Instrumental project and
3
+ # remove the # from the beginning of the following line to have instrument_server
4
+ # start sending metrics to your account.
5
+
6
+ api_key: <%= @api_key %>
@@ -0,0 +1,27 @@
1
+ #!/bin/sh
2
+ if ! which chef-solo; then
3
+ if [ -e "/etc/gentoo-release" ]; then
4
+ mkdir -p /usr/portage
5
+ emerge --sync
6
+ emerge --oneshot portage
7
+ emerge -C perl-core/Module-Metadata
8
+ emerge -C perl-core/Parse-CPAN-Meta
9
+ emerge -C lang-dev/perl
10
+ emerge perl
11
+ emerge layman
12
+ echo "source /var/lib/layman/make.conf" >> /etc/portage/make.conf
13
+ echo "conf_type : make.conf" >> /etc/layman/layman.cfg
14
+ layman -o https://raw.github.com/lxmx/gentoo-overlay/master/overlay.xml -f -a lxmx
15
+ echo "app-admin/chef-omnibus ~amd64" >> /etc/portage/package.keywords
16
+ emerge app-admin/chef-omnibus
17
+ else
18
+ if which curl; then
19
+ curl -L https://www.chef.io/chef/install.sh | bash
20
+ elif which wget; then
21
+ wget -qO- https://www.chef.io/chef/install.sh | bash
22
+ else
23
+ echo "Couldn't find curl or wget"
24
+ exit 1
25
+ fi
26
+ fi
27
+ fi
@@ -1,6 +1,6 @@
1
- ---
2
- # Replace YOUR_API_KEY with the API key for your Instrumental project and
3
- # remove the # from the beginning of the following line to have instrument_server
4
- # start sending metrics to your account.
5
-
6
- #api_key: YOUR_API_KEY
1
+ ---
2
+ # Replace YOUR_API_KEY with the API key for your Instrumental project and
3
+ # remove the # from the beginning of the following line to have instrument_server
4
+ # start sending metrics to your account.
5
+
6
+ #api_key: YOUR_API_KEY
@@ -5,3 +5,4 @@ The following scripts are examples of how to monitor other parts of your system
5
5
  * [MySQL](mysql/README.md)
6
6
  * [Docker](docker/README.md)
7
7
  * [Mongo](mongo/README.md)
8
+ * [Redis](redis/README.md)
@@ -0,0 +1,10 @@
1
+ # Redis Metrics
2
+
3
+ The [`redis_info.sh`](redis_info.sh) script generates metrics from the `redis-cli info` command for a redis instance. The following metrics will be output for every collection in your system:
4
+
5
+ * `connected_clients` - Total currently connected clients
6
+ * `instantaneous_ops_per_sec` - Current operations per second
7
+
8
+ Additionally, the following database level metrics will be output:
9
+
10
+ * `dbDB_NUM_keys` - Count of keys in the DB_NUM database
@@ -0,0 +1,10 @@
1
+ #! /bin/bash
2
+
3
+ REDIS_HOST=${REDIS_HOST:-"127.0.0.1"}
4
+ REDIS_PORT=${REDIS_PORT:-"6379"}
5
+
6
+ all_redis_info=`redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" info`
7
+ main_info=`echo "$all_redis_info" | grep -e "connected_clients" -e "instantaneous_ops_per_sec" | sed 's/:/ /g'`
8
+ db_key_info=`echo "$all_redis_info" | grep "keys=" | sed 's/:/_/g' | sed 's/=/ /g' | sed 's/,.*//g'`
9
+
10
+ printf "%s\r\n%s\r\n" "$main_info" "$db_key_info"
@@ -0,0 +1 @@
1
+ task :default
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'rubygems/command.rb'
3
+ require 'rubygems/dependency_installer.rb'
4
+
5
+ begin
6
+ Gem::Command.build_args = ARGV
7
+ rescue NoMethodError
8
+ end
9
+
10
+ inst = Gem::DependencyInstaller.new
11
+
12
+ begin
13
+ if RUBY_PLATFORM =~ /(win32|windows|mingw)/i
14
+ inst.install "wmi-lite", "~> 1.0.0"
15
+ end
16
+ rescue
17
+ exit(1)
18
+ end
@@ -33,11 +33,16 @@ Gem::Specification.new do |s|
33
33
  s.test_files = test_files
34
34
  s.executables = bin_files
35
35
  s.require_paths = ["lib"]
36
+ s.extensions = "ext/mkrf_conf.rb"
36
37
 
37
- s.required_ruby_version = '>= 1.9'
38
+ s.required_ruby_version = ">= 1.9"
38
39
 
39
- s.add_runtime_dependency(%q<instrumental_agent>, [">=0.12.6"])
40
+ s.add_runtime_dependency(%q<instrumental_agent>, [">=0.13.2"])
40
41
  s.add_runtime_dependency(%q<pidly>, [">=0.1.3"])
42
+ if ENV["INSTALL_WINDOWS"] || RUBY_PLATFORM =~ /(windows|win32|ming)/i
43
+ s.add_runtime_dependency(%q<wmi-lite>, [">=1.0.0"])
44
+ end
45
+
41
46
  s.add_development_dependency(%q<rake>, [">=0"])
42
47
  s.add_development_dependency(%q<fpm>, [">=1.3.3"])
43
48
  s.add_development_dependency(%q<package_cloud>, [">=0"])
@@ -48,4 +53,5 @@ Gem::Specification.new do |s|
48
53
  s.add_development_dependency(%q<librarian-puppet>, [">=0"])
49
54
  s.add_development_dependency(%q<puppet>, [">=0"])
50
55
  s.add_development_dependency(%q<serverspec>, [">=0"])
56
+ s.add_development_dependency(%q<winrm-transport>, ["~>1.0"])
51
57
  end
@@ -11,7 +11,7 @@ class MetricScriptExecutor
11
11
 
12
12
  def can_execute_file?(path)
13
13
  stat = File::Stat.new(path)
14
- stat.executable? && file_is_owner_only?(stat)
14
+ powershell_script?(path) || (stat.executable? && file_is_owner_only?(stat))
15
15
  end
16
16
 
17
17
  def can_execute_in_directory?(directory)
@@ -19,8 +19,39 @@ class MetricScriptExecutor
19
19
  stat.directory? && file_is_owner_only?(stat)
20
20
  end
21
21
 
22
+ def powershell_script?(path)
23
+ windows? && File.extname(path).to_s.downcase == ".ps1"
24
+ end
25
+
26
+ def windows?
27
+ RUBY_PLATFORM =~ /(win32|mswin|mingw)/
28
+ end
29
+
22
30
  def file_is_owner_only?(file_stat)
23
- file_stat.owned? && ((file_stat.mode & 0xFFF) ^ 0O700) == 0
31
+ owned_by_executor = file_stat.owned?
32
+ unless windows?
33
+ owned_by_executor && ((file_stat.mode & 0xFFF) ^ 0O700) == 0
34
+ else
35
+ owned_by_executor
36
+ end
37
+ end
38
+
39
+ def print_executable_warning(path)
40
+ if windows?
41
+ puts "[INFO] Cannot execute #{path}, must exist and be executable"
42
+ else
43
+ uid = Process.uid
44
+ user = Etc.getpwuid(uid).name
45
+ puts "[INFO] Cannot execute #{path}, must exist, be executable and only readable/writable by #{user}/#{uid}"
46
+ end
47
+ end
48
+
49
+ def print_executable_in_directory_warning(directory)
50
+ if windows?
51
+ puts "Directory #{directory} has gone away, not scanning for metric scripts."
52
+ else
53
+ puts "Directory #{directory} has gone away or does not have the correct permissions (0700), not scanning for metric scripts."
54
+ end
24
55
  end
25
56
 
26
57
  def execute_custom_script(full_path)
@@ -35,6 +66,16 @@ class MetricScriptExecutor
35
66
 
36
67
 
37
68
  cmd = [full_path, (previous_time || 0).to_i, (previous_status && previous_status.to_i)].compact.map(&:to_s)
69
+ if powershell_script?(full_path) && !File.executable?(full_path)
70
+ native_path = if File::ALT_SEPARATOR
71
+ full_path.split(File::SEPARATOR).join(File::ALT_SEPARATOR)
72
+ else
73
+ full_path
74
+ end
75
+ cmd[0] = native_path
76
+ cmd.unshift "-File"
77
+ cmd.unshift "powershell"
78
+ end
38
79
 
39
80
  pid = Process.spawn(*cmd,
40
81
  :chdir => File.dirname(full_path),
@@ -74,9 +115,7 @@ class MetricScriptExecutor
74
115
  execute_custom_script(full_path)
75
116
  else
76
117
  if !File.directory?(full_path)
77
- uid = Process.uid
78
- user = Etc.getpwuid(uid).name
79
- puts "[INFO] Cannot execute #{full_path}, must be executable and only readable/writable by #{user}/#{uid}"
118
+ print_executable_warning(full_path)
80
119
  end
81
120
  [full_path, []]
82
121
  end
@@ -84,7 +123,7 @@ class MetricScriptExecutor
84
123
  process_to_output = Hash[current]
85
124
  @previous = process_to_output
86
125
  else
87
- puts "Directory #{directory} has gone away or does not have the correct permissions (0700), not scanning for metric scripts."
126
+ print_executable_in_directory_warning(directory)
88
127
  end
89
128
  process_to_output.flat_map do |path, (status, time, output)|
90
129
  if status && status.success?
@@ -60,7 +60,8 @@ class ServerController < Pidly::Control
60
60
  end
61
61
 
62
62
  def build_agent(key, address, enabled)
63
- Instrumental::Agent.new(key, collector: address, enabled: enabled)
63
+ secure_protocol = address.split(':').last != '8000'
64
+ Instrumental::Agent.new(key, collector: address, enabled: enabled, secure: secure_protocol)
64
65
  end
65
66
 
66
67
  def set_new_agent(key, address)
@@ -148,6 +149,8 @@ class ServerController < Pidly::Control
148
149
  count += 1
149
150
  end
150
151
  end
152
+ agent.flush
153
+ agent.stop
151
154
  if debug?
152
155
  puts "Sent #{count} metrics"
153
156
  end
@@ -17,6 +17,9 @@ class SystemInspector
17
17
  when /darwin/
18
18
  require "instrumental_tools/system_inspector/osx"
19
19
  SystemInspector::OSX
20
+ when /(windows|win32|mingw)/
21
+ require "instrumental_tools/system_inspector/win32"
22
+ SystemInspector::Win32
20
23
  else
21
24
  raise "unsupported OS"
22
25
  end
@@ -0,0 +1,85 @@
1
+ require "wmi-lite"
2
+
3
+ class SystemInspector
4
+ module Win32
5
+
6
+ def self.wmi
7
+ @wmi ||= WmiLite::Wmi.new
8
+ end
9
+
10
+ def self.sanitize(name)
11
+ name.gsub(/[^a-z0-9\-\_]/i, "_").gsub(/(\A_+|_+\Z)/, "")
12
+ end
13
+
14
+ def self.load_cpu
15
+ cpus = wmi.query("SELECT PercentProcessorTime, PercentUserTime, PercentPrivilegedTime, PercentIdleTime, PercentInterruptTime FROM Win32_PerfFormattedData_PerfOS_Processor")
16
+ cpu_zero = cpus[0]
17
+ sysresult = wmi.query("SELECT ProcessorQueueLength FROM Win32_PerfFormattedData_PerfOS_System")
18
+ sys_stat = sysresult[0]
19
+ {
20
+ :gauges => {
21
+ "cpu.in_use" => cpu_zero["PercentProcessorTime"].to_f,
22
+ "cpu.user" => cpu_zero["PercentUserTime"].to_f,
23
+ "cpu.system" => cpu_zero["PercentPrivilegedTime"].to_f,
24
+ "cpu.idle" => cpu_zero["PercentIdleTime"].to_f,
25
+ "cpu.iowait" => cpu_zero["PercentInterruptTime"].to_f,
26
+ "load.processor_queue_length" => sys_stat["ProcessorQueueLength"].to_i
27
+ }
28
+ }
29
+ end
30
+
31
+ def self.load_memory
32
+ memresults = wmi.query("SELECT AvailableMBytes,CacheBytes FROM Win32_PerfFormattedData_PerfOS_Memory")
33
+ physmemory = wmi.query("SELECT Capacity FROM Win32_PhysicalMemory WHERE PoweredOn != false").reduce(0) { |memo, h| memo + h["Capacity"].to_f }
34
+ swapmemory = wmi.query("SELECT Name, PercentUsage FROM Win32_PerfFormattedData_PerfOS_PagingFile")
35
+ physmemory_mb = physmemory / 1024.0 / 1024.0
36
+ mainmemory = memresults[0]
37
+ avail_mb = mainmemory["AvailableMBytes"].to_f
38
+ used_mb = physmemory_mb - avail_mb
39
+ cached_mb = mainmemory["CacheBytes"].to_f / 1024.0 / 1024.0
40
+ free_perc = avail_mb / physmemory_mb
41
+ if free_perc.nan? || free_perc.infinite?
42
+ free_perc = 0
43
+ end
44
+ free_perc *= 100.0
45
+ memory_stats = {
46
+ "memory.used_mb" => used_mb,
47
+ "memory.free_mb" => avail_mb,
48
+ "memory.cached_mb" => cached_mb,
49
+ "memory.free_percent" => free_perc
50
+ }
51
+ swapmemory.each do |result|
52
+ formatted_name = sanitize(result["Name"])
53
+ used = result["PercentUsage"].to_f
54
+ memory_stats["swap.#{formatted_name}.free_percent"] = 100 - used
55
+ memory_stats["swap.#{formatted_name}.used_percent"] = used
56
+ end
57
+ { :gauges => memory_stats }
58
+ end
59
+
60
+ def self.load_disks
61
+ logical_disks = wmi.query("SELECT Name,FreeSpace,Size FROM Win32_LogicalDisk")
62
+ disk_stats = logical_disks.reduce({}) do |memo, disk|
63
+ name = sanitize(disk["Name"])
64
+ free_mb = disk["FreeSpace"].to_f / 1024.0 / 1024.0
65
+ size_mb = disk["Size"].to_f / 1024.0 / 1024.0
66
+ avail_perc = free_mb / size_mb
67
+ if avail_perc.nan? || avail_perc.infinite?
68
+ avail_perc = 0
69
+ end
70
+ avail_perc *= 100.0
71
+ memo.merge({
72
+ "disk.#{name}.total_mb" => size_mb,
73
+ "disk.#{name}.used_mb" => size_mb - free_mb,
74
+ "disk.#{name}.available_mb" => free_mb,
75
+ "disk.#{name}.available_percent" => avail_perc
76
+ })
77
+ end
78
+ { :gauges => disk_stats }
79
+ end
80
+
81
+ def self.load_filesystem
82
+ {}
83
+ end
84
+ end
85
+ end
@@ -1,5 +1,5 @@
1
1
  module Instrumental
2
2
  module Tools
3
- VERSION = "1.0.0"
3
+ VERSION = "1.1.3"
4
4
  end
5
5
  end
@@ -1,29 +1,45 @@
1
1
  require 'serverspec'
2
2
 
3
- set :backend, :exec
3
+ details = {}
4
4
 
5
-
6
- describe package('instrumental-tools') do
7
- it { should be_installed }
5
+ if RUBY_PLATFORM =~ /(win|mswin|mingw)/i
6
+ set :backend, :cmd
7
+ set :os, :family => 'windows'
8
+ details = {
9
+ check_owner: false,
10
+ config: "c:\\Program Files (x86)\\Instrumental Tools\\etc\\instrumental.yml",
11
+ has_pid: false,
12
+ service_name: "Instrument Server"
13
+ }
14
+ else
15
+ set :backend, :exec
16
+ details = {
17
+ check_owner: true,
18
+ config: "/etc/instrumental.yml",
19
+ has_pid: true,
20
+ pid_path: "/opt/instrumental-tools/instrument_server.pid",
21
+ owner: "nobody",
22
+ service_name: "instrument_server"
23
+ }
8
24
  end
9
25
 
10
- describe service('instrument_server') do
26
+ describe service(details[:service_name]) do
11
27
  it { should be_enabled }
12
28
  it { should be_running }
13
29
  end
14
30
 
15
- describe file('/opt/instrumental-tools/instrument_server.pid') do
16
- it { should be_file }
17
- it { should be_owned_by('nobody') }
31
+ if details[:has_pid]
32
+ describe file(details[:pid_path]) do
33
+ it { should be_file }
34
+ if details[:check_owner]
35
+ it { should be_owned_by(details[:owner]) }
36
+ end
37
+ end
18
38
  end
19
39
 
20
- describe file('/etc/instrumental.yml') do
40
+ describe file(details[:config]) do
21
41
  it { should be_file }
22
- it { should be_owned_by('nobody') }
23
- end
24
-
25
- describe process('ruby') do
26
- it { should be_running }
27
- its(:user) { should eq 'nobody' }
28
- its(:args) { should match /instrument_server/ }
42
+ if details[:check_owner]
43
+ it { should be_owned_by(details[:owner]) }
44
+ end
29
45
  end
@@ -0,0 +1,18 @@
1
+ prefix=.
2
+ MCS=mcs
3
+ MCSFLAGS=-reference:System.ServiceProcess.dll -reference:System.Configuration.dll -reference:System.Configuration.Install.dll
4
+ TARGET=build
5
+ SRC=src
6
+
7
+ all: InstrumentServer.exe
8
+
9
+ InstrumentServer.exe: $(SRC)/instrumental/*.cs
10
+ mkdir -p $(TARGET)
11
+ $(MCS) -out:$(TARGET)/$@ $(MCSFLAGS) $^
12
+
13
+ clean:
14
+ rm $(TARGET)/*
15
+
16
+ install: all
17
+ install -m 0755 $(TARGET)/InstrumentServer.exe $(prefix)/.
18
+
@@ -0,0 +1,242 @@
1
+ !include FileFunc.nsh
2
+ !include LogicLib.nsh
3
+ !include MUI.nsh
4
+ !include nsDialogs.nsh
5
+ !include x64.nsh
6
+
7
+
8
+ !define MUI_ICON "win32/logo.ico"
9
+
10
+ !insertmacro MUI_PAGE_DIRECTORY
11
+
12
+ Name "Instrumental Tools"
13
+ Icon "win32/logo.ico"
14
+ OutFile "<%= installer_file_name %>"
15
+
16
+ InstallDir "$PROGRAMFILES\Instrumental Tools"
17
+
18
+ Var Dialog
19
+ Var WelcomeLabel
20
+ Var KeyLabel
21
+ Var HostnameLabel
22
+ Var EnableScriptsLabel
23
+ Var ScriptDirectoryLabel
24
+ Var KeyText
25
+ Var HostnameText
26
+ Var EnableScriptsCheckbox
27
+ Var ScriptDirectoryRequest
28
+ Var ScriptDirectoryBrowser
29
+ Var ScriptDirectoryTemp
30
+
31
+ Var ApiKey
32
+ Var ScriptDirectoryDestination
33
+ Var EnableScripts
34
+ Var Hostname
35
+ Var ServiceCommand
36
+
37
+ Var DotNetVersion
38
+ Var InstallUtilPath
39
+ Var DotNetFriendlyVersion
40
+ Var DotNetInstall
41
+
42
+
43
+ Function .onInit
44
+
45
+ StrCpy $DotNetFriendlyVersion "4.0"
46
+ StrCpy $DotNetInstall "http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9cfb2d51-5ff4-4491-b0e5-b386f32c0992&displaylang=en"
47
+ StrCpy $DotNetVersion "v4.0.30319"
48
+ StrCpy $InstallUtilPath "$WINDIR\Microsoft.NET\Framework\$DotNetVersion\installUtil.exe"
49
+
50
+ Call AssertDotNet
51
+
52
+ ${GetOptions} $CMDLINE "/K" $ApiKey
53
+ IfErrors 0 +2
54
+ StrCpy $ApiKey "Your API Key"
55
+
56
+ ${GetOptions} $CMDLINE "/H" $Hostname
57
+ IfErrors 0 +2
58
+ Call GetComputerName
59
+
60
+ ${GetOptions} $CMDLINE "/E" $0
61
+ IfErrors 0 +2
62
+ StrCpy $EnableScripts 1
63
+ StrCpy $EnableScripts 0
64
+
65
+
66
+ ${GetOptions} $CMDLINE "/SD" $ScriptDirectoryDestination
67
+ IfErrors 0 +2
68
+ StrCpy $ScriptDirectoryDestination "$INSTDIR\Scripts"
69
+
70
+ FunctionEnd
71
+
72
+ Function un.onInit
73
+ StrCpy $DotNetFriendlyVersion "4.0"
74
+ StrCpy $DotNetInstall "http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9cfb2d51-5ff4-4491-b0e5-b386f32c0992&displaylang=en"
75
+ StrCpy $DotNetVersion "v4.0.30319"
76
+ StrCpy $InstallUtilPath "$WINDIR\Microsoft.NET\Framework\$DotNetVersion\installUtil.exe"
77
+ FunctionEnd
78
+
79
+ Function GetComputerName
80
+ ReadRegStr $0 HKLM "System\CurrentControlSet\Control\ComputerName\ActiveComputerName" "ComputerName"
81
+ StrCmp $0 "" win9x
82
+ StrCpy $1 $0 4 3
83
+ Goto done
84
+ win9x:
85
+ ReadRegStr $0 HKLM "System\CurrentControlSet\Control\ComputerName\ComputerName" "ComputerName"
86
+ StrCpy $1 $0 4 3
87
+ done:
88
+ StrCpy $Hostname $0
89
+ FunctionEnd
90
+
91
+ Function WriteConfigFile
92
+ FileOpen $4 "$INSTDIR\etc\instrumental.yml" "w"
93
+ FileWrite $4 "---$\r$\n"
94
+ FileWrite $4 "# Replace YOUR_API_KEY with the API key for your Instrumental project and$\r$\n"
95
+ FileWrite $4 "# remove the # from the beginning of the following line to have instrument_server$\r$\n"
96
+ FileWrite $4 "# start sending metrics to your account.$\r$\n"
97
+ FileWrite $4 "$\r$\n"
98
+ FileWrite $4 "api_key: $ApiKey$\r$\n"
99
+ FileWrite $4 "$\r$\n"
100
+ FileClose $4
101
+ FunctionEnd
102
+
103
+ Page custom nsDialogsPage nsDialogsPageLeave
104
+ Page instfiles
105
+
106
+ Function AssertDotNet
107
+ IfFileExists $InstallUtilPath NextStep
108
+ MessageBox MB_OK|MB_ICONEXCLAMATION "You must have the Microsoft .NET Framework $DotNetFriendlyVersion Installed to use this application. $\n$\n The installer will now open the Microsft .NET Framework $DotNetFriendlyVersion webpage"
109
+ ExecShell Open "$DotNetInstall" SW_SHOWNORMAL
110
+ Quit
111
+ NextStep:
112
+ FunctionEnd
113
+
114
+ Function BrowseDest
115
+ nsDialogs::SelectFolderDialog "Select Folder" $ScriptDirectoryDestination
116
+ Pop $0
117
+ ${If} $0 != error
118
+ StrCpy $ScriptDirectoryDestination $0
119
+ ${NSD_SetText} $ScriptDirectoryRequest $ScriptDirectoryDestination
120
+ ${EndIf}
121
+ FunctionEnd
122
+
123
+ Function EnDisableButton
124
+ ${NSD_GetState} $EnableScriptsCheckbox $0
125
+ ${If} $0 == 1
126
+ StrCpy $EnableScripts 1 1
127
+ EnableWindow $ScriptDirectoryBrowser 1
128
+ EnableWindow $ScriptDirectoryRequest 1
129
+ ${Else}
130
+ StrCpy $EnableScripts 0 1
131
+ EnableWindow $ScriptDirectoryBrowser 0
132
+ EnableWindow $ScriptDirectoryRequest 0
133
+ ${EndIf}
134
+ FunctionEnd
135
+
136
+
137
+ Function nsDialogsPage
138
+ !insertmacro MUI_HEADER_TEXT "Monitoring Options" "Enter your configuration details below. API key information is available on your account on https://instrumentalapp.com/."
139
+ nsDialogs::Create 1018
140
+ Pop $Dialog
141
+
142
+ ${If} $Dialog == error
143
+ Abort
144
+ ${EndIf}
145
+
146
+ ${NSD_CreateLabel} 0 0 100% 12u "Enter configuration details below"
147
+ Pop $WelcomeLabel
148
+
149
+ ${NSD_CreateLabel} 0 13u 30% 12u "API Key"
150
+ Pop $KeyLabel
151
+
152
+ ${NSD_CreateText} 35% 13u 60% 12u $ApiKey
153
+ Pop $KeyText
154
+
155
+ ${NSD_CreateLabel} 0 26u 30% 12u "Hostname"
156
+ Pop $HostnameLabel
157
+
158
+ ${NSD_CreateText} 35% 26u 60% 12u $Hostname
159
+ Pop $HostnameText
160
+
161
+ ${NSD_CreateLabel} 0 39u 30% 12u "Enable Custom Scripts?"
162
+ Pop $EnableScriptsLabel
163
+
164
+ ${NSD_CreateCheckbox} 35% 39u 60% 12u ""
165
+ Pop $EnableScriptsCheckbox
166
+
167
+ ${NSD_CreateLabel} 0 52u 30% 12u "Script Directory"
168
+ Pop $ScriptDirectoryLabel
169
+
170
+ ${NSD_CreateDirRequest} 35% 52u 40% 12u $ScriptDirectoryDestination
171
+ Pop $ScriptDirectoryRequest
172
+
173
+ ${NSD_CreateBrowseButton} 77% 52u 10% 12u ".."
174
+ Pop $ScriptDirectoryBrowser
175
+
176
+ StrCmp $EnableScripts 1 0 nomatch
177
+ ${NSD_Check} $EnableScriptsCheckbox
178
+ Call EnDisableButton
179
+ nomatch:
180
+ Call EnDisableButton
181
+
182
+ ${NSD_OnClick} $EnableScriptsCheckbox EnDisableButton
183
+ ${NSD_OnClick} $ScriptDirectoryRequest BrowseDest
184
+ ${NSD_OnClick} $ScriptDirectoryBrowser BrowseDest
185
+
186
+ nsDialogs::Show
187
+
188
+ FunctionEnd
189
+
190
+ Function nsDialogsPageLeave
191
+
192
+ ${NSD_GetText} $HostnameText $Hostname
193
+ ${NSD_GetText} $KeyText $ApiKey
194
+ ${NSD_GetState} $EnableScriptsCheckbox $EnableScripts
195
+ ${NSD_GetText} $ScriptDirectoryRequest $ScriptDirectoryDestination
196
+
197
+
198
+ FunctionEnd
199
+
200
+
201
+ Section "Installing Files"
202
+ SetOutPath $INSTDIR
203
+ <% directories.each do |directory| %>
204
+ File /r <%= directory %>/*.*
205
+ <% end %>
206
+ <% files, _ = removable_artifacts %>
207
+ <% files.each do |file| %>
208
+ SetFileAttributes "$INSTDIR\\<%= file %>" NORMAL
209
+ <% if file.index("Gemfile.lock") %>
210
+ Exec 'icacls "$INSTDIR\\<%= file %>" /grant BUILTIN\Users:f'
211
+ <% end %>
212
+ <% end %>
213
+ WriteUninstaller $INSTDIR\<%= uninstaller_name %>
214
+ Call WriteConfigFile
215
+
216
+
217
+ ${If} $EnableScripts == 1
218
+ CreateDirectory "$ScriptDirectoryDestination"
219
+ Exec '"$InstallUtilPath" /Hostname="$Hostname" /ScriptsDirectory="$ScriptDirectoryDestination" /ScriptsEnabled="True" /Path="$INSTDIR" /Config="$INSTDIR\etc\instrumental.yml" InstrumentServer.exe'
220
+ ${Else}
221
+ Exec '"$InstallUtilPath" /Path="$INSTDIR" /Config="$INSTDIR\etc\instrumental.yml" /Hostname="$Hostname" InstrumentServer.exe'
222
+ ${EndIf}
223
+
224
+ Exec '"sc" start "Instrument Server"'
225
+ SectionEnd
226
+
227
+ Section "Uninstall"
228
+ Exec '"sc" stop "Instrument Server"'
229
+ Exec '"$InstallUtilPath" /u "$INSTDIR\InstrumentServer.exe"'
230
+ <% files, directories = removable_artifacts %>
231
+ <% files.each do |file| %>
232
+ delete "$INSTDIR\\<%= file %>"
233
+ <% end %>
234
+ delete "$INSTDIR\\<%= uninstaller_name %>"
235
+ <% directories.each do |directory| %>
236
+ <% if directory.strip.empty? %>
237
+ rmdir "$INSTDIR"
238
+ <% else %>
239
+ rmdir /R "$INSTDIR\\<%= directory %>"
240
+ <% end %>
241
+ <% end %>
242
+ SectionEnd