Skip to content

Commit

Permalink
Merge pull request #64 from SumoLogic/222461-installation-logs
Browse files Browse the repository at this point in the history
Add collection and upload of installation script logs
  • Loading branch information
ccressent authored Jun 3, 2024
2 parents 4138166 + 42cd587 commit 74ff6d8
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 50 deletions.
68 changes: 61 additions & 7 deletions install-script/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ param (
# The API URL used to communicate with the SumoLogic backend
[string] $Api,

# DisableInstallationTelemetry is used to disable reporting the installation
# to Sumologic.
[bool] $DisableInstallationTelemetry,

# InstallationLogfileEndpoint is used to configure the endpoint where
# installation logs will be sent.
[string] $InstallationLogfileEndpoint,

# The OpAmp Endpoint used to communicate with the OpAmp backend
[string] $OpAmpApi
)
Expand Down Expand Up @@ -399,19 +407,48 @@ function Get-BinaryFromUri {
Write-Host "Downloaded ${Path}"
}

function Send-Installation-Logs {
param (
[Parameter(Mandatory, Position=0)]
[HttpClient] $HttpClient,

[Parameter(Mandatory, Position=1)]
[string] $Endpoint,

[Parameter(Mandatory, Position=2)]
[string] $Path
)

$Content = Get-Content -Path $Path
$StringContent = [System.Net.Http.StringContent]::new($Content)

$response = $HttpClient.PostAsync($Endpoint, $StringContent).GetAwaiter().GetResult()
if (!($response.IsSuccessStatusCode)) {
$statusCode = [int]$response.StatusCode
$reasonPhrase = $response.StatusCode.ToString()
$errMsg = "${statusCode} ${reasonPhrase}"

if ($response.Content -ne $null) {
$content = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
$errMsg += ": ${content}"
}

Write-Error $errMsg -ErrorAction Stop
}
}

##
# Main code
##

try {
if ($InstallationToken -eq $null -or $InstallationToken -eq "") {
Write-Error "Installation token has not been provided. Please set the SUMOLOGIC_INSTALLATION_TOKEN environment variable." -ErrorAction Stop
$InstallationLogFile = New-TemporaryFile

if ($InstallationLogFileEndpoint -eq "") {
$InstallationLogFileEndpoint = "https://open-events.sumologic.net/api/v1/collector/installation/logs"
}

$osName = Get-OSName
$archName = Get-ArchName
Write-Host "Detected OS type:`t${osName}"
Write-Host "Detected architecture:`t${archName}"
Start-Transcript $InstallationLogFile | Out-Null

$handler = New-Object HttpClientHandler
$handler.AllowAutoRedirect = $true
Expand All @@ -423,6 +460,15 @@ try {
# set http client timeout to 30 seconds
$httpClient.Timeout = New-Object System.TimeSpan(0, 0, 30)

if ($InstallationToken -eq $null -or $InstallationToken -eq "") {
Write-Error "Installation token has not been provided. Please set the SUMOLOGIC_INSTALLATION_TOKEN environment variable." -ErrorAction Stop
}

$osName = Get-OSName
$archName = Get-ArchName
Write-Host "Detected OS type:`t${osName}"
Write-Host "Detected architecture:`t${archName}"

if ($Fips -eq $true) {
if ($osName -ne "Win32NT" -or $archName -ne "x64") {
Write-Error "Error: The FIPS-approved binary is only available for windows/amd64"
Expand Down Expand Up @@ -533,4 +579,12 @@ try {
msiexec.exe /i "$msiPath" /passive $msiProperties
} catch [HttpRequestException] {
Write-Error $_.Exception.InnerException.Message
}
} finally {
Stop-Transcript | Out-Null

if ($DisableInstallationTelemetry -eq $false) {
Send-Installation-Logs -Endpoint $InstallationLogFileEndpoint -Path $InstallationLogFile -HttpClient $httpClient
}

Remove-Item $InstallationLogFile
}
40 changes: 37 additions & 3 deletions install-script/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ ARG_SHORT_EPHEMERAL='E'
ARG_LONG_EPHEMERAL='ephemeral'
ARG_SHORT_TIMEOUT='m'
ARG_LONG_TIMEOUT='download-timeout'
ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY='S'
ARG_LONG_DISABLE_INSTALLATION_TELEMETRY='disable-installation-telemetry'
ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT='l'
ARG_LONG_INSTALLATION_LOGFILE_ENDPOINT='installation-logfile-endpoint'

PACKAGE_GITHUB_ORG="SumoLogic"
PACKAGE_GITHUB_REPO="sumologic-otel-collector-packaging"
Expand All @@ -67,6 +71,8 @@ readonly ARG_SHORT_INSTALL_HOSTMETRICS ARG_LONG_INSTALL_HOSTMETRICS
readonly ARG_SHORT_REMOTELY_MANAGED ARG_LONG_REMOTELY_MANAGED
readonly ARG_SHORT_EPHEMERAL ARG_LONG_EPHEMERAL
readonly ARG_SHORT_TIMEOUT ARG_LONG_TIMEOUT
readonly ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY ARG_LONG_DISABLE_INSTALLATION_TELEMETRY
readonly ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT ARG_LONG_INSTALLATION_LOGFILE_ENDPOINT
readonly DEPRECATED_ARG_LONG_TOKEN DEPRECATED_ENV_TOKEN DEPRECATED_ARG_LONG_SKIP_TOKEN
readonly PACKAGE_GITHUB_ORG PACKAGE_GITHUB_REPO

Expand Down Expand Up @@ -131,6 +137,10 @@ CURL_MAX_TIME=1800
# set by check_dependencies therefore cannot be set by set_defaults
SYSTEMD_DISABLED=false

DISABLE_INSTALLATION_TELEMETRY=false
INSTALLATION_LOGFILE="${TMPDIR:=/tmp}/sumologic-otel-collector_installation.log"
INSTALLATION_LOGFILE_ENDPOINT='https://open-events.sumologic.net/api/v1/collector/installation/logs'

############################ Functions

function usage() {
Expand Down Expand Up @@ -164,6 +174,7 @@ Supported arguments:
-${ARG_SHORT_EPHEMERAL}, --${ARG_LONG_EPHEMERAL} Delete the collector from Sumo Logic after 12 hours of inactivity.
-${ARG_SHORT_TIMEOUT}, --${ARG_LONG_TIMEOUT} <timeout> Timeout in seconds after which download will fail. Default is ${CURL_MAX_TIME}.
-${ARG_SHORT_YES}, --${ARG_LONG_YES} Disable confirmation asks.
-${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}, --${ARG_LONG_DISABLE_INSTALLATION_TELEMETRY} Do not report installation logs to Sumologic.
-${ARG_SHORT_HELP}, --${ARG_LONG_HELP} Prints this help and usage.
Expand All @@ -172,6 +183,18 @@ Supported env variables:
EOF
}

# shellcheck disable=SC2317 # Don't warn about unreachable commands in this function
# ShellCheck may incorrectly believe that code is unreachable if it's invoked in
# a trap, like the reporter function.
function reporter {
if ! $DISABLE_INSTALLATION_TELEMETRY; then
echo "SUMOLOGIC_INSTALLATION_TOKEN=${SUMOLOGIC_INSTALLATION_TOKEN}" >> "$INSTALLATION_LOGFILE"
curl --silent --location -X POST --data-binary @"${INSTALLATION_LOGFILE}" "${INSTALLATION_LOGFILE_ENDPOINT}"
rm -f "${INSTALLATION_LOGFILE}"
fi
}
trap reporter EXIT

function set_defaults() {
HOME_DIRECTORY="/var/lib/otelcol-sumo"
FILE_STORAGE="${HOME_DIRECTORY}/file_storage"
Expand Down Expand Up @@ -269,7 +292,7 @@ function parse_options() {
"--${ARG_LONG_TIMEOUT}")
set -- "$@" "-${ARG_SHORT_TIMEOUT}"
;;
"-${ARG_SHORT_TOKEN}"|"-${ARG_SHORT_HELP}"|"-${ARG_SHORT_API}"|"-${ARG_SHORT_OPAMP_API}"|"-${ARG_SHORT_TAG}"|"-${ARG_SHORT_SKIP_CONFIG}"|"-${ARG_SHORT_VERSION}"|"-${ARG_SHORT_FIPS}"|"-${ARG_SHORT_YES}"|"-${ARG_SHORT_SKIP_SYSTEMD}"|"-${ARG_SHORT_UNINSTALL}"|"-${ARG_SHORT_PURGE}"|"-${ARG_SHORT_SKIP_TOKEN}"|"-${ARG_SHORT_DOWNLOAD}"|"-${ARG_SHORT_CONFIG_BRANCH}"|"-${ARG_SHORT_BINARY_BRANCH}"|"-${ARG_SHORT_BRANCH}"|"-${ARG_SHORT_KEEP_DOWNLOADS}"|"-${ARG_SHORT_TIMEOUT}"|"-${ARG_SHORT_INSTALL_HOSTMETRICS}"|"-${ARG_SHORT_REMOTELY_MANAGED}"|"-${ARG_SHORT_EPHEMERAL}")
"-${ARG_SHORT_TOKEN}"|"-${ARG_SHORT_HELP}"|"-${ARG_SHORT_API}"|"-${ARG_SHORT_OPAMP_API}"|"-${ARG_SHORT_TAG}"|"-${ARG_SHORT_SKIP_CONFIG}"|"-${ARG_SHORT_VERSION}"|"-${ARG_SHORT_FIPS}"|"-${ARG_SHORT_YES}"|"-${ARG_SHORT_SKIP_SYSTEMD}"|"-${ARG_SHORT_UNINSTALL}"|"-${ARG_SHORT_PURGE}"|"-${ARG_SHORT_SKIP_TOKEN}"|"-${ARG_SHORT_DOWNLOAD}"|"-${ARG_SHORT_CONFIG_BRANCH}"|"-${ARG_SHORT_BINARY_BRANCH}"|"-${ARG_SHORT_BRANCH}"|"-${ARG_SHORT_KEEP_DOWNLOADS}"|"-${ARG_SHORT_TIMEOUT}"|"-${ARG_SHORT_INSTALL_HOSTMETRICS}"|"-${ARG_SHORT_REMOTELY_MANAGED}"|"-${ARG_SHORT_EPHEMERAL}"|"-${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}"|"-${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}")
set -- "$@" "${arg}"
;;
"--${ARG_LONG_INSTALL_HOSTMETRICS}")
Expand All @@ -281,6 +304,12 @@ function parse_options() {
"--${ARG_LONG_EPHEMERAL}")
set -- "$@" "-${ARG_SHORT_EPHEMERAL}"
;;
"--${ARG_LONG_DISABLE_INSTALLATION_TELEMETRY}")
set -- "$@" "-${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}"
;;
"--${ARG_LONG_INSTALLATION_LOGFILE_ENDPOINT}")
set -- "$@" "-${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}"
;;
-*)
echo "Unknown option ${arg}"; usage; exit 1 ;;
*)
Expand All @@ -293,7 +322,7 @@ function parse_options() {

while true; do
set +e
getopts "${ARG_SHORT_HELP}${ARG_SHORT_TOKEN}:${ARG_SHORT_API}:${ARG_SHORT_OPAMP_API}:${ARG_SHORT_TAG}:${ARG_SHORT_VERSION}:${ARG_SHORT_FIPS}${ARG_SHORT_YES}${ARG_SHORT_SKIP_SYSTEMD}${ARG_SHORT_UNINSTALL}${ARG_SHORT_PURGE}${ARG_SHORT_SKIP_TOKEN}${ARG_SHORT_SKIP_CONFIG}${ARG_SHORT_DOWNLOAD}${ARG_SHORT_KEEP_DOWNLOADS}${ARG_SHORT_CONFIG_BRANCH}:${ARG_SHORT_BINARY_BRANCH}:${ARG_SHORT_BRANCH}:${ARG_SHORT_EPHEMERAL}${ARG_SHORT_REMOTELY_MANAGED}${ARG_SHORT_INSTALL_HOSTMETRICS}${ARG_SHORT_TIMEOUT}:" opt
getopts "${ARG_SHORT_HELP}${ARG_SHORT_TOKEN}:${ARG_SHORT_API}:${ARG_SHORT_OPAMP_API}:${ARG_SHORT_TAG}:${ARG_SHORT_VERSION}:${ARG_SHORT_FIPS}${ARG_SHORT_YES}${ARG_SHORT_SKIP_SYSTEMD}${ARG_SHORT_UNINSTALL}${ARG_SHORT_PURGE}${ARG_SHORT_SKIP_TOKEN}${ARG_SHORT_SKIP_CONFIG}${ARG_SHORT_DOWNLOAD}${ARG_SHORT_KEEP_DOWNLOADS}${ARG_SHORT_CONFIG_BRANCH}:${ARG_SHORT_BINARY_BRANCH}:${ARG_SHORT_BRANCH}:${ARG_SHORT_EPHEMERAL}${ARG_SHORT_REMOTELY_MANAGED}${ARG_SHORT_INSTALL_HOSTMETRICS}${ARG_SHORT_TIMEOUT}:${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}:" opt
set -e

# Invalid argument catched, print and exit
Expand All @@ -305,7 +334,7 @@ function parse_options() {

# Validate opt and set arguments
case "$opt" in
"${ARG_SHORT_HELP}") usage; exit 0 ;;
"${ARG_SHORT_HELP}") usage; DISABLE_INSTALLATION_TELEMETRY=true exit 0 ;;
"${ARG_SHORT_TOKEN}") SUMOLOGIC_INSTALLATION_TOKEN="${OPTARG}" ;;
"${ARG_SHORT_API}") API_BASE_URL="${OPTARG}" ;;
"${ARG_SHORT_OPAMP_API}") OPAMP_API_URL="${OPTARG}" ;;
Expand All @@ -332,6 +361,8 @@ function parse_options() {
"${ARG_SHORT_EPHEMERAL}") EPHEMERAL=true ;;
"${ARG_SHORT_KEEP_DOWNLOADS}") KEEP_DOWNLOADS=true ;;
"${ARG_SHORT_TIMEOUT}") CURL_MAX_TIME="${OPTARG}" ;;
"${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}") DISABLE_INSTALLATION_TELEMETRY=true ;;
"${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}") INSTALLATION_LOGFILE_ENDPOINT="${OPTARG}" ;;
"${ARG_SHORT_TAG}")
if [[ "${OPTARG}" != ?*"="* ]]; then
echo "Invalid tag: '${OPTARG}'. Should be in 'key=value' format"
Expand Down Expand Up @@ -1746,6 +1777,9 @@ function plutil_replace_key() {

############################ Main code

# Redirect a copy of stdout and stderr into $INSTALLATION_LOGFILE
exec > >(tee "${INSTALLATION_LOGFILE}") 2>&1

OS_TYPE="$(get_os_type)"
ARCH_TYPE="$(get_arch_type)"
readonly OS_TYPE ARCH_TYPE
Expand Down
62 changes: 55 additions & 7 deletions install-script/test/check.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package sumologic_scripts_tests

import (
"context"
"io/fs"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
Expand All @@ -10,14 +13,20 @@ import (
"github.com/stretchr/testify/require"
)

type mockInstallationLogsEndpoint struct {
server *http.Server
receivedData bool
}

type check struct {
test *testing.T
installOptions installOptions
code int
err error
expectedInstallCode int
output []string
errorOutput []string
test *testing.T
installationLogsEndpoint *mockInstallationLogsEndpoint
installOptions installOptions
code int
err error
expectedInstallCode int
output []string
errorOutput []string
}

type condCheckFunc func(check) bool
Expand Down Expand Up @@ -228,3 +237,42 @@ func PathHasUserACL(t *testing.T, path string, ownerName string, perms string) {
require.NoError(t, err, "error while checking "+path+" acl")
require.Contains(t, string(output), "user:"+ownerName+":"+perms)
}

func preActionStartInstallationLogsMockReceiver(c check) {
c.test.Log("Starting mock installation logs endpoint")

mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost && r.ContentLength >= 1 {
c.installationLogsEndpoint.receivedData = true
}
})

listener, err := net.Listen("tcp", ":4444")
require.NoError(c.test, err)

c.installationLogsEndpoint.server = &http.Server{
Handler: mux,
}

go func() {
err := c.installationLogsEndpoint.server.Serve(listener)
if err != nil && err != http.ErrServerClosed {
require.NoError(c.test, err)
}
}()
}

func checkInstallationLogsReceived(c check) {
require.Equal(c.test, true, c.installationLogsEndpoint.receivedData, "mock installation logs endpoint should have received data but didn't")

c.test.Log("Stopping mock installation logs endpoint")
require.NoError(c.test, c.installationLogsEndpoint.server.Shutdown(context.Background()))
}

func checkInstallationLogsNotReceived(c check) {
require.Equal(c.test, false, c.installationLogsEndpoint.receivedData, "mock installation logs endpoint received data but shouldn't have")

c.test.Log("Stopping mock installation logs endpoint")
require.NoError(c.test, c.installationLogsEndpoint.server.Shutdown(context.Background()))
}
50 changes: 31 additions & 19 deletions install-script/test/command_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,27 @@ import (
)

type installOptions struct {
installToken string
autoconfirm bool
skipSystemd bool
tags map[string]string
skipConfig bool
skipInstallToken bool
fips bool
envs map[string]string
uninstall bool
purge bool
apiBaseURL string
configBranch string
downloadOnly bool
dontKeepDownloads bool
installHostmetrics bool
remotelyManaged bool
ephemeral bool
timeout float64
opampEndpoint string
installToken string
autoconfirm bool
skipSystemd bool
tags map[string]string
skipConfig bool
skipInstallToken bool
fips bool
envs map[string]string
uninstall bool
purge bool
apiBaseURL string
configBranch string
downloadOnly bool
dontKeepDownloads bool
installHostmetrics bool
remotelyManaged bool
ephemeral bool
timeout float64
opampEndpoint string
disableInstallationTelemetry bool
installationLogfileEndpoint string
}

func (io *installOptions) string() []string {
Expand Down Expand Up @@ -106,6 +108,16 @@ func (io *installOptions) string() []string {
opts = append(opts, "--download-timeout", fmt.Sprintf("%f", io.timeout))
}

if io.disableInstallationTelemetry {
opts = append(opts, "--disable-installation-telemetry")
}

if io.installationLogfileEndpoint == "" {
opts = append(opts, "--installation-logfile-endpoint", StagingInstallationLogfileEndpoint)
} else {
opts = append(opts, "--installation-logfile-endpoint", io.installationLogfileEndpoint)
}

if io.opampEndpoint != "" {
opts = append(opts, "--opamp-api", io.opampEndpoint)
}
Expand Down
Loading

0 comments on commit 74ff6d8

Please sign in to comment.