Skip to content

Commit

Permalink
test: add basic Windows install script tests
Browse files Browse the repository at this point in the history
  • Loading branch information
swiatekm committed Mar 6, 2024
1 parent 0d36aa0 commit fd14043
Show file tree
Hide file tree
Showing 9 changed files with 483 additions and 9 deletions.
2 changes: 0 additions & 2 deletions pkg/scripts_test/check.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build !windows

package sumologic_scripts_tests

import (
Expand Down
65 changes: 65 additions & 0 deletions pkg/scripts_test/check_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package sumologic_scripts_tests

import (
"os/user"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func checkAbortedDueToNoToken(c check) {
require.Greater(c.test, len(c.output), 1)
require.Greater(c.test, len(c.errorOutput), 1)
require.Contains(c.test, c.errorOutput[0], "Installation token has not been provided.")
require.Contains(c.test, c.errorOutput[1], "Please set the SUMOLOGIC_INSTALLATION_TOKEN environment variable.")
}

func checkEphemeralNotInConfig(p string) func(c check) {
return func(c check) {
assert.False(c.test, c.installOptions.ephemeral, "ephemeral was specified")

conf, err := getConfig(p)
require.NoError(c.test, err, "error while reading configuration")

assert.False(c.test, conf.Extensions.Sumologic.Ephemeral, "ephemeral is true")
}
}

func checkEphemeralInConfig(p string) func(c check) {
return func(c check) {
assert.True(c.test, c.installOptions.ephemeral, "ephemeral was not specified")

conf, err := getConfig(p)
require.NoError(c.test, err, "error while reading configuration")

assert.True(c.test, conf.Extensions.Sumologic.Ephemeral, "ephemeral is not true")
}
}

func checkTokenInConfig(c check) {
require.NotEmpty(c.test, c.installOptions.installToken, "installation token has not been provided")

conf, err := getConfig(userConfigPath)
require.NoError(c.test, err, "error while reading configuration")

require.Equal(c.test, c.installOptions.installToken, conf.Extensions.Sumologic.InstallationToken, "installation token is different than expected")
}

func checkTokenInSumoConfig(c check) {
require.NotEmpty(c.test, c.installOptions.installToken, "installation token has not been provided")

conf, err := getConfig(configPath)
require.NoError(c.test, err, "error while reading configuration")

require.Equal(c.test, c.installOptions.installToken, conf.Extensions.Sumologic.InstallationToken, "installation token is different than expected")
}

func checkUserExists(c check) {
_, err := user.Lookup(systemUser)
require.NoError(c.test, err, "user has not been created")
}

func checkUserNotExists(c check) {
_, err := user.Lookup(systemUser)
require.Error(c.test, err, "user has been created")
}
2 changes: 1 addition & 1 deletion pkg/scripts_test/command_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !windows
//go:build linux || darwin

package sumologic_scripts_tests

Expand Down
167 changes: 167 additions & 0 deletions pkg/scripts_test/command_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package sumologic_scripts_tests

import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"strings"

"github.com/stretchr/testify/require"
)

type installOptions struct {
installToken string
tags map[string]string
fips bool
envs map[string]string
apiBaseURL string
installHostmetrics bool
remotelyManaged bool
ephemeral bool
}

func (io *installOptions) string() []string {
opts := []string{
"-Command",
scriptPath,
}

if io.fips {
opts = append(opts, "-Fips", "1")
}

if io.installHostmetrics {
opts = append(opts, "-InstallHostMetrics", "1")
}

if io.remotelyManaged {
opts = append(opts, "-RemotelyManaged", "1")
}

if io.ephemeral {
opts = append(opts, "-Ephemeral", "1")
}

if len(io.tags) > 0 {
opts = append(opts, "-Tags", getTagOptValue(io.tags))
}

if io.apiBaseURL != "" {
opts = append(opts, "-Api", io.apiBaseURL)
}

return opts
}

func (io *installOptions) buildEnvs() []string {
e := os.Environ()

for k, v := range io.envs {
e = append(e, fmt.Sprintf("%s=%s", k, v))
}

if io.installToken != "" {
e = append(e, fmt.Sprintf("%s=%s", installTokenEnv, io.installToken))
}

return e
}

func exitCode(cmd *exec.Cmd) (int, error) {
err := cmd.Wait()

if err == nil {
return cmd.ProcessState.ExitCode(), nil
}

if exiterr, ok := err.(*exec.ExitError); ok {
return exiterr.ExitCode(), nil
}

return 0, fmt.Errorf("cannot obtain exit code: %v", err)
}

func runScript(ch check) (int, []string, []string, error) {
cmd := exec.Command("powershell", ch.installOptions.string()...)
cmd.Env = ch.installOptions.buildEnvs()
output := []string{}

in, err := cmd.StdinPipe()
if err != nil {
require.NoError(ch.test, err)
}

defer in.Close()

out, err := cmd.StdoutPipe()
if err != nil {
require.NoError(ch.test, err)
}
defer out.Close()

errOut, err := cmd.StderrPipe()
if err != nil {
require.NoError(ch.test, err)
}
defer errOut.Close()

// We want to read line by line
bufOut := bufio.NewReader(out)

// Start the process
if err = cmd.Start(); err != nil {
require.NoError(ch.test, err)
}

// Read the results from the process
for {
line, _, err := bufOut.ReadLine()
strLine := strings.TrimSpace(string(line))

if len(strLine) > 0 {
output = append(output, strLine)
}
ch.test.Log(strLine)

// exit if script finished
if err == io.EOF {
break
}

// otherwise ensure there is no error
require.NoError(ch.test, err)

}

// Handle stderr separately
bufErrOut := bufio.NewReader(errOut)
errorOutput := []string{}
for {
line, _, err := bufErrOut.ReadLine()
strLine := strings.TrimSpace(string(line))

if len(strLine) > 0 {
errorOutput = append(errorOutput, strLine)
}
ch.test.Log(strLine)

// exit if script finished
if err == io.EOF {
break
}
}

code, err := exitCode(cmd)
return code, output, errorOutput, err
}

func getTagOptValue(tags map[string]string) string {
tagOpts := []string{}
for k, v := range tags {
tagOpts = append(tagOpts, fmt.Sprintf("%s = \"%s\"", k, v))
}
tagOptString := strings.Join(tagOpts, " ; ")
return fmt.Sprintf("@{ %s }", tagOptString)
}
2 changes: 0 additions & 2 deletions pkg/scripts_test/common.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build !windows

package sumologic_scripts_tests

type testSpec struct {
Expand Down
89 changes: 89 additions & 0 deletions pkg/scripts_test/common_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//go:build windows

package sumologic_scripts_tests

import (
"context"
"fmt"
"io"
"net"
"net/http"
"os/exec"
"testing"

"github.com/stretchr/testify/require"
)

// These checks always have to be true after a script execution
var commonPostChecks = []checkFunc{checkNoBakFilesPresent}

func runTest(t *testing.T, spec *testSpec) {
ch := check{
test: t,
installOptions: spec.options,
expectedInstallCode: spec.installCode,
}

t.Log("Running conditional checks")
for _, a := range spec.conditionalChecks {
if !a(ch) {
t.SkipNow()
}
}

defer tearDown(t)

t.Log("Starting HTTP server")
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, err := io.WriteString(w, "200 OK\n")
require.NoError(t, err)
})

listener, err := net.Listen("tcp", ":3333")
require.NoError(t, err)

httpServer := &http.Server{
Handler: mux,
}
go func() {
err := httpServer.Serve(listener)
if err != nil && err != http.ErrServerClosed {
require.NoError(t, err)
}
}()
defer func() {
require.NoError(t, httpServer.Shutdown(context.Background()))
}()

t.Log("Running pre actions")
for _, a := range spec.preActions {
a(ch)
}

t.Log("Running pre checks")
for _, c := range spec.preChecks {
c(ch)
}

ch.code, ch.output, ch.errorOutput, ch.err = runScript(ch)

checkRun(ch)

t.Log("Running common post checks")
for _, c := range commonPostChecks {
c(ch)
}

t.Log("Running post checks")
for _, c := range spec.postChecks {
c(ch)
}
}

func tearDown(t *testing.T) {
cmd := exec.Command("powershell", "Uninstall-Package", "-Name", fmt.Sprintf(`"%s"`, packageName))
if out, err := cmd.CombinedOutput(); err != nil {
t.Log(string(out))
}
}
2 changes: 0 additions & 2 deletions pkg/scripts_test/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build !windows

package sumologic_scripts_tests

import (
Expand Down
32 changes: 32 additions & 0 deletions pkg/scripts_test/consts_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build windows

package sumologic_scripts_tests

const (
systemGroup string = "otelcol-sumo"
systemUser string = "otelcol-sumo"

packageName string = "OpenTelemetry Collector"

binaryPath string = `C:\Program Files\Sumo Logic\OpenTelemetry Collector\bin\otelcol-sumo.exe`
libPath string = `C:\ProgramData\Sumo Logic\OpenTelemetry Collector\data`
fileStoragePath string = libPath + `\file_storage`
etcPath string = `C:\ProgramData\Sumo Logic\OpenTelemetry Collector\config`
scriptPath string = "../../scripts/install.ps1"
configPath = etcPath + `\sumologic.yaml`
confDPath = etcPath + `\conf.d`
opampDPath = etcPath + `\opamp.d`
userConfigPath = confDPath + `\common.yaml`
hostmetricsConfigPath = confDPath + `\hostmetrics.yaml`

installToken string = "token"
installTokenEnv string = "SUMOLOGIC_INSTALLATION_TOKEN"
apiBaseURL string = "https://open-collectors.sumologic.com"

commonConfigPathFilePermissions uint32 = 0550
configPathDirPermissions uint32 = 0550
configPathFilePermissions uint32 = 0440
confDPathFilePermissions uint32 = 0644
etcPathPermissions uint32 = 0551
opampDPermissions uint32 = 0750
)
Loading

0 comments on commit fd14043

Please sign in to comment.