Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 67 additions & 12 deletions install-script/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 @@ -346,14 +354,60 @@ 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}"
Copy link

@chan-tim-sumo chan-tim-sumo Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this continue to run even after the error message pops up? should the error message get thrown? i saw write-error on line 408:

        Write-Error "Installation token has not been provided." -ErrorAction Stop


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

}
}#
#
# Main code
##

try {
$InstallationLogFile = New-TemporaryFile

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

Start-Transcript $InstallationLogFile | Out-Null

$handler = New-Object HttpClientHandler
$handler.AllowAutoRedirect = $true

$httpClient = New-Object System.Net.Http.HttpClient($handler)
$userAgentHeader = New-Object System.Net.Http.Headers.ProductInfoHeaderValue("otelcol-sumo-installer", "0.1")
$httpClient.DefaultRequestHeaders.UserAgent.Add($userAgentHeader)

# 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
Write-Host "Detected OS type:`t${osName}"
Expand All @@ -373,16 +427,6 @@ try {
Write-Host "Architecture overridden: `t${archName}"
}

$handler = New-Object HttpClientHandler
$handler.AllowAutoRedirect = $true

$httpClient = New-Object System.Net.Http.HttpClient($handler)
$userAgentHeader = New-Object System.Net.Http.Headers.ProductInfoHeaderValue("otelcol-sumo-installer", "0.1")
$httpClient.DefaultRequestHeaders.UserAgent.Add($userAgentHeader)

# set http client timeout to 30 seconds
$httpClient.Timeout = New-Object System.TimeSpan(0, 0, 30)

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" -ErrorAction Stop
Expand Down Expand Up @@ -473,6 +517,17 @@ try {
msiexec.exe /i "$msiPath" /passive $msiProperties
} catch [HttpRequestException] {
Write-Error $_.Exception.InnerException.Message -ErrorAction Stop
} finally {
Stop-Transcript | Out-Null
if ($InstallationToken -eq $true) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not too familiar with powershell, i'm assuming this line means if installation token equals to true?

is installation token a boolean?

Add-Content -Path $InstallationLogFile.FullName -Value $InstallationToken
}
if ($DisableInstallationTelemetry -eq $false) {
Send-Installation-Logs -Endpoint $InstallationLogFileEndpoint -Path $InstallationLogFile -HttpClient $httpClient
}

Remove-Item $InstallationLogFile

}

Write-Host "Installation successful"
43 changes: 40 additions & 3 deletions install-script/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ ARG_SHORT_CLOBBER='C'
ARG_LONG_CLOBBER='clobber'
ARG_SHORT_PACKAGE_PATH='P'
ARG_LONG_PACKAGE_PATH='package-path'
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'

readonly ARG_SHORT_TOKEN ARG_LONG_TOKEN ARG_SHORT_HELP ARG_LONG_HELP ARG_SHORT_API ARG_LONG_API
readonly ARG_SHORT_TAG ARG_LONG_TAG ARG_SHORT_VERSION ARG_LONG_VERSION ARG_SHORT_YES ARG_LONG_YES
Expand All @@ -73,6 +77,8 @@ readonly ARG_SHORT_TIMEZONE ARG_LONG_TIMEZONE
readonly ARG_SHORT_CLOBBER ARG_LONG_CLOBBER
readonly ARG_SHORT_PACKAGE_PATH ARG_LONG_PACKAGE_PATH
readonly DEPRECATED_ARG_LONG_TOKEN DEPRECATED_ENV_TOKEN DEPRECATED_ARG_LONG_SKIP_TOKEN
readonly ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY ARG_LONG_DISABLE_INSTALLATION_TELEMETRY
readonly ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT ARG_LONG_INSTALLATION_LOGFILE_ENDPOINT

############################ Variables (see set_defaults function for default values)

Expand Down Expand Up @@ -138,6 +144,11 @@ PACKAGECLOUD_MASTER_TOKEN="${PACKAGECLOUD_MASTER_TOKEN:-}"
# built and not the latest package uploaded to S3.
DARWIN_PKG_URL="${DARWIN_PKG_URL:-}"


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

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

function usage() {
Expand Down Expand Up @@ -173,6 +184,7 @@ Supported arguments:
-${ARG_SHORT_CLOBBER}, --${ARG_LONG_CLOBBER} Overwrite existing installation without asking for confirmation.
-${ARG_SHORT_PACKAGE_PATH}, --${ARG_LONG_PACKAGE_PATH} <path> Install package from file path instead of fetching it.
-${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 @@ -181,6 +193,19 @@ Supported env variables:
EOF
}

# 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.
# shellcheck disable=SC2317,SC2119,SC2120,SC2317,SC2329,SC1072,SC1073,SC1125
function reporter {
if ! $DISABLE_INSTALLATION_TELEMETRY; then
echo "SUMOLOGIC_INSTALLATION_TOKEN=${SUMOLOGIC_INSTALLATION_TOKEN}" >> "$INSTALLATION_LOGFILE"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is writing the installation token to the log file to /tmp safe?

i saw in the repo

function set_tmpdir() {
    # generate a new tmpdir using mktemp
    # need to specify the template for some MacOS versions
    TMPDIR=$(mktemp -d -t 'sumologic-otel-collector-XXXX')
}

i was looking at what mktemp is, and it creates a secure temporary file. would using something like that be better?

curl --silent --location -X POST --data-binary @"${INSTALLATION_LOGFILE}" "${INSTALLATION_LOGFILE_ENDPOINT}"
rm -f "${INSTALLATION_LOGFILE}"
fi
}
trap reporter EXIT

function set_defaults() {
DOWNLOAD_CACHE_DIR="/var/cache/otelcol-sumo" # this is in case we want to keep downloaded binaries
CONFIG_DIRECTORY="/etc/otelcol-sumo"
Expand Down Expand Up @@ -269,7 +294,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_VERSION}"|"-${ARG_SHORT_FIPS}"|"-${ARG_SHORT_YES}"|"-${ARG_SHORT_UNINSTALL}"|"-${ARG_SHORT_UPGRADE}"|"-${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_TIMEZONE}"|"-${ARG_SHORT_CLOBBER}"|"-${ARG_SHORT_PACKAGE_PATH}")
"-${ARG_SHORT_TOKEN}"|"-${ARG_SHORT_HELP}"|"-${ARG_SHORT_API}"|"-${ARG_SHORT_OPAMP_API}"|"-${ARG_SHORT_TAG}"|"-${ARG_SHORT_VERSION}"|"-${ARG_SHORT_FIPS}"|"-${ARG_SHORT_YES}"|"-${ARG_SHORT_UNINSTALL}"|"-${ARG_SHORT_UPGRADE}"|"-${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_TIMEZONE}"|"-${ARG_SHORT_CLOBBER}"|"-${ARG_SHORT_PACKAGE_PATH}"|"-${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}"|"-${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}")
set -- "$@" "${arg}"
;;
"--${ARG_LONG_INSTALL_HOSTMETRICS}")
Expand All @@ -290,6 +315,12 @@ function parse_options() {
"--${ARG_LONG_PACKAGE_PATH}")
set -- "$@" "-${ARG_SHORT_PACKAGE_PATH}"
;;
"--${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 2 ;;
*)
Expand All @@ -302,7 +333,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_UPGRADE}${ARG_SHORT_UNINSTALL}${ARG_SHORT_PURGE}${ARG_SHORT_SKIP_TOKEN}${ARG_SHORT_DOWNLOAD}${ARG_SHORT_KEEP_DOWNLOADS}${ARG_SHORT_CONFIG_BRANCH}:${ARG_SHORT_BINARY_BRANCH}:${ARG_SHORT_BRANCH}:${ARG_SHORT_EPHEMERAL}${ARG_SHORT_TIMEZONE}:${ARG_SHORT_CLOBBER}${ARG_SHORT_REMOTELY_MANAGED}${ARG_SHORT_INSTALL_HOSTMETRICS}${ARG_SHORT_TIMEOUT}:${ARG_SHORT_PACKAGE_PATH}:" 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_UPGRADE}${ARG_SHORT_UNINSTALL}${ARG_SHORT_PURGE}${ARG_SHORT_SKIP_TOKEN}${ARG_SHORT_DOWNLOAD}${ARG_SHORT_KEEP_DOWNLOADS}${ARG_SHORT_CONFIG_BRANCH}:${ARG_SHORT_BINARY_BRANCH}:${ARG_SHORT_BRANCH}:${ARG_SHORT_EPHEMERAL}${ARG_SHORT_TIMEZONE}:${ARG_SHORT_CLOBBER}${ARG_SHORT_REMOTELY_MANAGED}${ARG_SHORT_INSTALL_HOSTMETRICS}${ARG_SHORT_TIMEOUT}:${ARG_SHORT_PACKAGE_PATH}:${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}:" opt
set -e

# Invalid argument catched, print and exit
Expand All @@ -314,7 +345,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 @@ -339,6 +370,8 @@ function parse_options() {
"${ARG_SHORT_EPHEMERAL}") EPHEMERAL=true ;;
"${ARG_SHORT_TIMEZONE}") TIMEZONE="${OPTARG}" ;;
"${ARG_SHORT_CLOBBER}") CLOBBER=true ;;
"${ARG_SHORT_DISABLE_INSTALLATION_TELEMETRY}") DISABLE_INSTALLATION_TELEMETRY=true ;;
"${ARG_SHORT_INSTALLATION_LOGFILE_ENDPOINT}") INSTALLATION_LOGFILE_ENDPOINT="${OPTARG}" ;;
"${ARG_SHORT_KEEP_DOWNLOADS}") KEEP_DOWNLOADS=true ;;
"${ARG_SHORT_TIMEOUT}") CURL_MAX_TIME="${OPTARG}" ;;
"${ARG_SHORT_TAG}") FIELDS+=("${OPTARG}") ;;
Expand Down Expand Up @@ -1134,6 +1167,10 @@ function get_user_token() {

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

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


if [ "${S3_BUCKET}" = "sumologic-osc-stable" ]; then
DOWNLOAD_URI="$CDN_URI"
else
Expand Down
66 changes: 59 additions & 7 deletions install-script/test/check.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
package sumologic_scripts_tests

import (
"context"
"io/fs"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"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 @@ -275,3 +284,46 @@ func PathHasUserACL(t *testing.T, path string, ownerName string, perms string) b
}
return assert.Contains(t, string(output), "user:"+ownerName+":"+perms)
}

func preActionStartInstallationLogsMockReceiver(c check) bool {
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)
}
}()

return true
}

func checkInstallationLogsReceived(c check) bool {
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()))
return true
}

func checkInstallationLogsNotReceived(c check) bool {
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()))
return true
}
54 changes: 35 additions & 19 deletions install-script/test/command_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,31 @@ import (
)

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

func (io *installOptions) string() []string {
Expand Down Expand Up @@ -108,6 +114,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
Loading