fleet/orbit/pkg/update/runner_test.go
Victor Lyuboslavsky 3367b7e036
Added orbit_version, fleet_desktop_version, and scripts_enabled to host details. (#18123)
#17361
#17148

In GET fleet/hosts/:id response, added the following fields:
- orbit_version
  - `orbit_version == null` means this agent is not an orbit agent
- fleet_desktop_version
- `fleet_desktop_version == null` means this agent is not an orbit agent
or it is an older version which is not collecting the desktop version
- `fleet_desktop_version == ""` means this agent is an orbit agent but
does not have fleet desktop
- scripts_enabled
- `scripts_enabled == null` means this agent is not an orbit agent or it
is an older version which is not collecting scripts_enabled

In orbit_info table, added the following fields:
- desktop_version
- scripts_enabled

Updated docs for orbit_info PR:
https://github.com/fleetdm/fleet/pull/18135

Updated API docs: https://github.com/fleetdm/fleet/pull/17814

MDM lock/unlock/wipe error messages are not part of this PR. They will
be in a separate PR.

# Checklist for submitter
- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Added support on fleet's osquery simulator `cmd/osquery-perf` for
new osquery data ingestion features.
- [x] Added/updated tests
- [x] If database migrations are included, checked table schema to
confirm autoupdate
- [x] Manual QA for all new/changed functionality
  - For Orbit and Fleet Desktop changes:
- [x] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.
- [x] Auto-update manual QA, from released version of component to new
version (see [tools/tuf/test](../tools/tuf/test/README.md)).
2024-04-09 16:33:44 -05:00

226 lines
6 KiB
Go

package update
import (
"math/rand"
"os"
"runtime"
"strings"
"testing"
"time"
"github.com/fleetdm/fleet/v4/pkg/nettest"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewRunner(t *testing.T) {
// TODO(lucas): Do not use our TUF remote repository
// but instead create local repository and serve with a httptest server.
// For that, we need to move and export some functionality currently in
// "ee/fleetctl/updates.go" (as it doesn't make sense to have such functionality
// there and import such eefleetctl package here).
nettest.Run(t)
rootDir := t.TempDir()
updateOpts := DefaultOptions
updateOpts.RootDirectory = rootDir
u, err := NewUpdater(updateOpts)
require.NoError(t, err)
err = u.UpdateMetadata()
require.NoError(t, err)
runnerOpts := RunnerOptions{
CheckInterval: 1 * time.Second,
Targets: []string{"osqueryd"},
}
// NewRunner should not fail if targets do not exist locally.
r, err := NewRunner(u, runnerOpts)
require.NoError(t, err)
execPath, err := u.ExecutableLocalPath("osqueryd")
require.NoError(t, err)
require.NoFileExists(t, execPath)
// r.UpdateAction should download osqueryd.
didUpdate, err := r.UpdateAction()
require.NoError(t, err)
require.True(t, didUpdate)
require.FileExists(t, execPath)
// Create another Runner but with the target already existing.
r2, err := NewRunner(u, runnerOpts)
require.NoError(t, err)
didUpdate, err = r2.UpdateAction()
require.NoError(t, err)
require.False(t, didUpdate)
}
func TestRandomizeDuration(t *testing.T) {
rand, err := randomizeDuration(10 * time.Minute)
require.NoError(t, err)
assert.True(t, rand >= 0)
assert.True(t, rand < 10*time.Minute)
}
func TestGetVersion(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on Windows")
}
t.Parallel()
testCases := map[string]struct {
cmd string
version string
}{
"4.5.6": {
cmd: "#!/bin/bash\n/bin/echo orbit 4.5.6",
version: "4.5.6",
},
"42.0.0": {
cmd: "#!/bin/bash\n/bin/echo fleet-desktop 42.0.0",
version: "42.0.0",
},
"5.10.2-26-gc396d07b4-dirty": {
cmd: "#!/bin/bash\n/bin/echo osquery version 5.10.2-26-gc396d07b4-dirty",
version: "5.10.2-26-gc396d07b4-dirty",
},
"bad output": {
cmd: "#!/bin/bash\n/bin/echo osquery version is weird",
version: "",
},
"bad cmd": {
cmd: "bozo+bozo+bozo",
version: "",
},
}
for name, tc := range testCases {
tc := tc // capture range variable, needed for parallel tests
t.Run(
name, func(t *testing.T) {
t.Parallel()
// create a temp executable file
dir := t.TempDir()
file, err := os.CreateTemp(dir, "binary")
require.NoError(t, err)
_, err = file.WriteString(tc.cmd)
require.NoError(t, err)
err = file.Chmod(0755)
require.NoError(t, err)
_ = file.Close()
// "text file busy" is a Go issue when executing file just written: https://github.com/golang/go/issues/22315
var version string
retries := 0
for {
version, err = GetVersion(file.Name())
if err != nil {
t.Log(err)
if strings.Contains(err.Error(), "text file busy") {
if retries > 5 {
t.Fatal("too many retries due to 'text file busy' error: https://github.com/golang/go/issues/22315")
}
// adding some randomization so that parallel tests get out of sync if needed
time.Sleep((500 + time.Duration(rand.Intn(100))) * time.Millisecond) //nolint:gosec
retries++
} else {
break
}
} else {
break
}
}
assert.Equal(t, tc.version, version)
},
)
}
}
func TestCompareVersion(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on Windows")
}
t.Parallel()
testCases := map[string]struct {
cmd string
oldVersion string
expected *int
}{
"downgrade": {
cmd: "#!/bin/bash\n/bin/echo orbit 4.9",
oldVersion: "4.10",
expected: ptr.Int(1),
},
"same": {
cmd: "#!/bin/bash\n/bin/echo osquery version 5.10.2-26-gc396d07b4-dirty",
oldVersion: "5.10.2-26-gc396d07b4-dirty",
expected: ptr.Int(0),
},
"same 2": {
cmd: "#!/bin/bash\n/bin/echo osquery version 5.10",
oldVersion: "5.10.0",
expected: ptr.Int(0),
},
"upgrade": {
cmd: "#!/bin/bash\n/bin/echo osquery version 5.10.10",
oldVersion: "5.10.9",
expected: ptr.Int(-1),
},
"invalid new version": {
cmd: "#!/bin/bash\n/bin/echo osquery version invalid",
oldVersion: "5.10.9",
expected: nil,
},
"invalid old version": {
cmd: "#!/bin/bash\n/bin/echo orbit 1",
oldVersion: "",
expected: nil,
},
"invalid old version 2": {
cmd: "#!/bin/bash\n/bin/echo orbit 1",
oldVersion: "1.01", // invalid, needs to be 1.1
expected: nil,
},
}
for name, tc := range testCases {
tc := tc // capture range variable, needed for parallel tests
t.Run(
name, func(t *testing.T) {
t.Parallel()
// create a temp executable file
dir := t.TempDir()
file, err := os.CreateTemp(dir, "binary")
require.NoError(t, err)
_, err = file.WriteString(tc.cmd)
require.NoError(t, err)
err = file.Chmod(0755)
require.NoError(t, err)
_ = file.Close()
// "text file busy" is a Go issue when executing file just written: https://github.com/golang/go/issues/22315
var result *int
retries := 0
for {
result, err = compareVersion(file.Name(), tc.oldVersion, "target")
if err != nil {
t.Log(err)
if strings.Contains(err.Error(), "text file busy") {
if retries > 5 {
t.Fatal("too many retries due to 'text file busy' error: https://github.com/golang/go/issues/22315")
}
// adding some randomization so that parallel tests get out of sync if needed
time.Sleep((500 + time.Duration(rand.Intn(100))) * time.Millisecond) //nolint:gosec
retries++
} else {
break
}
} else {
break
}
}
assert.Equal(t, tc.expected, result)
},
)
}
}