Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add msi builder #19

Merged
merged 15 commits into from Sep 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -17,6 +17,23 @@ iis | [Win32_PerfRawData_W3SVC_WebService](https://msdn.microsoft.com/en-us/libr

The HELP texts shows the WMI data source, please see MSDN documentation for details.

## Installation
Each release provides a .msi installer. The installer will setup the WMI Exporter as a Windows service, as well as create an exception in the Windows Firewall.

If the installer is run without any parameters, the exporter will run with default settings for enabled collectors, ports, etc. The following parameters are available:

Name | Description
-----|------------
`ENABLED_COLLECTORS` | As the `-collectors.enabled` flag, provide a comma-separated list of enabled collectors
`LISTEN_ADDR` | The IP address to bind to. Defaults to 0.0.0.0
`LISTEN_PORT` | The port to bind to. Defaults to 9182.
`METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics`

Parameters are sent to the installer via `msiexec`. Example invocation:

```powershell
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,iis LISTEN_PORT=5000
```

## Roadmap

Expand Down
34 changes: 23 additions & 11 deletions appveyor.yml
Expand Up @@ -19,17 +19,29 @@ install:
- go get -u github.com/kardianos/govendor

build_script:
- govendor build -v +local
- govendor build -v +p
- govendor test -v +local
- ps: |
if($env:APPVEYOR_REPO_TAG -eq "True") {
# The MSI version is not semver compliant, so just take the numerical parts
$Version = $env:APPVEYOR_REPO_TAG_NAME -replace '^v?([0-9\.]+).*$','$1'
Write-Verbose "Setting msi version to $Version"
.\installer\build.ps1 -PathToExecutable .\wmi_exporter.exe -Version $Version -Arch "amd64"
Push-AppveyorArtifact installer\Output\wmi_exporter-$Version-amd64.msi -DeploymentName Installer
}

artifacts:
- name: Executable
path: wmi_exporter.exe

deploy:
release: wmi_exporter-v$(appveyor_build_version)
description: 'WMI exporter for prometheus'
provider: GitHub
auth_token:
secure: CrXWeTf7qONUOEki5olFfGEUPMLDeHj61koDXV3OVEaLgtACmnVHsKUub9POflda # encrypted token from GitHub
draft: false
prerelease: false
on:
branch: master # release from master branch only
appveyor_repo_tag: true # deploy on tag push only
- provider: GitHub
description: WMI Exporter version $(appveyor_build_version)
artifact: Executable,Installer
auth_token:
secure: 'CrXWeTf7qONUOEki5olFfGEUPMLDeHj61koDXV3OVEaLgtACmnVHsKUub9POflda'
draft: false
prerelease: false
on:
branch: master
appveyor_repo_tag: true
68 changes: 61 additions & 7 deletions exporter.go
Expand Up @@ -10,6 +10,8 @@ import (
"sync"
"time"

"golang.org/x/sys/windows/svc"

"github.com/martinlindhe/wmi_exporter/collector"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
Expand All @@ -23,6 +25,7 @@ type WmiCollector struct {

const (
defaultCollectors = "logical_disk,os"
serviceName = "wmi_exporter"
)

var (
Expand Down Expand Up @@ -134,15 +137,22 @@ func main() {
return
}

isInteractive, err := svc.IsAnInteractiveSession()
if err != nil {
log.Fatal(err)
}

stopCh := make(chan bool)
if !isInteractive {
go svc.Run(serviceName, &wmiExporterService{stopCh: stopCh})
}

collectors, err := loadCollectors(*enabledCollectors)
if err != nil {
log.Fatalf("Couldn't load collectors: %s", err)
}

log.Infof("Enabled collectors:")
for n := range collectors {
log.Infof(" - %s", n)
}
log.Infof("Enabled collectors: %v", strings.Join(keys(collectors), ", "))

nodeCollector := WmiCollector{collectors: collectors}
prometheus.MustRegister(nodeCollector)
Expand All @@ -155,8 +165,52 @@ func main() {
log.Infoln("Starting WMI exporter", version.Info())
log.Infoln("Build context", version.BuildContext())

log.Infoln("Listening on", *listenAddress)
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
log.Fatalf("cannot start WMI exporter: %s", err)
go func() {
log.Infoln("Starting server on", *listenAddress)
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
log.Fatalf("cannot start WMI exporter: %s", err)
}
}()

for {
if <-stopCh {
log.Info("Shutting down WMI exporter")
break
}
}
}

func keys(m map[string]collector.Collector) []string {
ret := make([]string, 0, len(m))
for key, _ := range m {
ret = append(ret, key)
}
return ret
}

type wmiExporterService struct {
stopCh chan<- bool
}

func (s *wmiExporterService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
s.stopCh <- true
break loop
default:
log.Error(fmt.Sprintf("unexpected control request #%d", c))
}
}
}
changes <- svc.Status{State: svc.StopPending}
return
}
4 changes: 4 additions & 0 deletions installer/.gitignore
@@ -0,0 +1,4 @@
Work/*
Output/*
WiX/*
Source/*
61 changes: 61 additions & 0 deletions installer/build.ps1
@@ -0,0 +1,61 @@
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[String] $PathToExecutable,
[Parameter(Mandatory=$true)]
[String] $Version,
[Parameter(Mandatory=$false)]
[ValidateSet("amd64","386")]
[String] $Arch = "amd64"
)
$ErrorActionPreference = "Stop"

# Get absolute path to executable before switching directories
$PathToExecutable = Resolve-Path $PathToExecutable
# Set working dir to this directory, reset previous on exit
Push-Location $PSScriptRoot
Trap {
# Reset working dir on error
Pop-Location
}

if ($PSVersionTable.PSVersion.Major -lt 5) {
Write-Error "Powershell version 5 required"
exit 1
}

$wc = New-Object System.Net.WebClient
function Get-FileIfNotExists {
Param (
$Url,
$Destination
)
if(-not (Test-Path $Destination)) {
Write-Verbose "Downloading $Url"
$wc.DownloadFile($Url, $Destination)
}
else {
Write-Verbose "${Destination} already exists. Skipping."
}
}

$sourceDir = mkdir -Force Source
mkdir -Force Work,Output | Out-Null

Write-Verbose "Downloading files"
# Somewhat obscure url, points to WiX 3.10 binary release
Write-Verbose "Downloading WiX..."
Get-FileIfNotExists "http://download-codeplex.sec.s-msft.com/Download/Release?ProjectName=wix&DownloadId=1504735&FileTime=130906491728530000&Build=21031" "$sourceDir\wix-binaries.zip"
mkdir -Force WiX | Out-Null
Expand-Archive -Path "${sourceDir}\wix-binaries.zip" -DestinationPath WiX -Force

Copy-Item -Force $PathToExecutable Work/wmi_exporter.exe

Write-Verbose "Creating wmi_exporter-${Version}-${Arch}.msi"
$wixArch = @{"amd64"="x64"; "386"="x86"}[$Arch]
$wixOpts = "-ext WixFirewallExtension"
Invoke-Expression "WiX\candle.exe -nologo -arch $wixArch $wixOpts -out Work\wmi_exporter.wixobj -dVersion=`"$Version`" wmi_exporter.wxs"
Invoke-Expression "WiX\light.exe -nologo -spdb $wixOpts -out `"Output\wmi_exporter-${Version}-${Arch}.msi`" Work\wmi_exporter.wixobj"

Write-Verbose "Done!"
Pop-Location
48 changes: 48 additions & 0 deletions installer/wmi_exporter.wxs
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:fw="http://schemas.microsoft.com/wix/FirewallExtension">
<?if $(sys.BUILDARCH)=x64 ?>
<?define PlatformProgramFiles = "ProgramFiles64Folder" ?>
<?else ?>
<?define PlatformProgramFiles = "ProgramFilesFolder" ?>
<?endif ?>

<Product Id="*" UpgradeCode="66a6eb5b-1fc2-4b14-a362-5ceec6413308"
Name="WMI Exporter" Version="$(var.Version)" Manufacturer="Martin Lindhe"
Language="1033" Codepage="1252">
<Package Id="*" Manufacturer="Martin Lindhe" InstallScope="perMachine"
Description="WMI Exporter $(var.Version) installer" Compressed="yes" />
<Media Id="1" Cabinet="wmi_exporter.cab" EmbedCab="yes"/>
<MajorUpgrade Schedule="afterInstallExecute" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />

<Property Id="ENABLED_COLLECTORS" Secure="yes"/>
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="-collectors.enabled [ENABLED_COLLECTORS]">ENABLED_COLLECTORS</SetProperty>

<Property Id="LISTEN_ADDR" Secure="yes" />
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="-telemetry.addr [LISTEN_ADDR]:[LISTEN_PORT]">LISTEN_ADDR OR LISTEN_PORT</SetProperty>

<Property Id="METRICS_PATH" Secure="yes"/>
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="-telemetry.path [METRICS_PATH]">METRICS_PATH</SetProperty>

<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFiles)">
<Directory Id="APPLICATIONROOTDIRECTORY" Name="wmi_exporter" />
</Directory>
</Directory>

<ComponentGroup Id="Files" Directory="APPLICATIONROOTDIRECTORY">
<Component>
<File Id="wmi_exporter.exe" Name="wmi_exporter.exe" Source="Work\wmi_exporter.exe" KeyPath="yes">
<fw:FirewallException Id="MetricsEndpoint" Name="WMI Exporter (HTTP [LISTEN_PORT])" Description="WMI Exporter HTTP endpoint" Port="[LISTEN_PORT]" Protocol="tcp" Scope="any" />
</File>
<ServiceInstall Id="InstallExporterService" Name="wmi_exporter" ErrorControl="normal" Start="auto" Type="ownProcess" Arguments="-log.format logger:eventlog?name=wmi_exporter [CollectorsFlag] [ListenFlag] [MetricsPathFlag]" />
<ServiceControl Id="ServiceStateControl" Name="wmi_exporter" Remove="uninstall" Start="install" Stop="both" />
</Component>
</ComponentGroup>

<Feature Id="DefaultFeature" Level="1">
<ComponentGroupRef Id="Files" />
</Feature>
</Product>
</Wix>
89 changes: 89 additions & 0 deletions vendor/github.com/prometheus/common/log/eventlog_formatter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.