Skip to content

Commit c264d61

Browse files
author
Kaan Yalti
committed
enhancement(5235): added unpack error handling tests in upgrade_test
enhancement(5235): updated the unpack tests
1 parent f23ac81 commit c264d61

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

internal/pkg/agent/application/upgrade/upgrade_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
package upgrade
66

77
import (
8+
"bytes"
89
"context"
910
"crypto/tls"
1011
"fmt"
12+
"io"
13+
"net/http"
14+
"net/http/httptest"
1115
"os"
1216
"path/filepath"
1317
"runtime"
@@ -26,6 +30,8 @@ import (
2630
"github.com/elastic/elastic-agent-libs/transport/tlscommon"
2731
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
2832
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact"
33+
downloadErrors "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/errors"
34+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/common"
2935
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
3036
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
3137
"github.com/elastic/elastic-agent/internal/pkg/config"
@@ -38,6 +44,7 @@ import (
3844
"github.com/elastic/elastic-agent/pkg/core/logger"
3945
"github.com/elastic/elastic-agent/pkg/core/logger/loggertest"
4046
agtversion "github.com/elastic/elastic-agent/pkg/version"
47+
"github.com/elastic/elastic-agent/testing/mocks/internal_/pkg/agent/application/info"
4148
mocks "github.com/elastic/elastic-agent/testing/mocks/pkg/control/v2/client"
4249
)
4350

@@ -1349,3 +1356,139 @@ func createArchive(t *testing.T, archiveName string, archiveFiles []files) (stri
13491356
}
13501357
return createTarArchive(t, archiveName, archiveFiles)
13511358
}
1359+
1360+
func TestUpgradeUnpackErrors(t *testing.T) {
1361+
log, _ := loggertest.New("test")
1362+
1363+
tempConfig := &artifact.Config{} // used only to get os and arch, runtime.GOARCH returns amd64 which is not a valid arch when used in GetArtifactName
1364+
1365+
targetVersion := agtversion.NewParsedSemVer(3, 4, 5, "SNAPSHOT", "")
1366+
targetArtifactName, err := artifact.GetArtifactName(agentArtifact, *targetVersion, tempConfig.OS(), tempConfig.Arch())
1367+
require.NoError(t, err)
1368+
1369+
targetArchiveFiles := modifyArchiveFiles(archiveFilesWithMoreComponents,
1370+
archiveFilesWithArchiveDirName(targetArtifactName),
1371+
archiveFilesWithVersionedHome(targetVersion.CoreVersion(), "ghijkl"),
1372+
)
1373+
1374+
// Get the content of the component file to be used in copy function
1375+
// assertions. It will be used to make sure the error gets triggered in the
1376+
// copy call in the unpacker and not the downloader.
1377+
targetArchiveComponentsFileContent := ""
1378+
for _, file := range targetArchiveFiles {
1379+
if file.fType == REGULAR {
1380+
dir := filepath.Dir(file.path)
1381+
if strings.HasSuffix(dir, "components") {
1382+
targetArchiveComponentsFileContent = file.content
1383+
break
1384+
}
1385+
}
1386+
}
1387+
1388+
// dir name of the versioned home
1389+
// used to calculate component paths
1390+
newVersionedDirName := "elastic-agent-3.4.5-SNAPSHOT-ghijkl"
1391+
1392+
mockAgentInfo := info.NewAgent(t)
1393+
mockAgentInfo.On("Version").Return(targetVersion.String())
1394+
1395+
upgradeDetails := details.NewDetails(targetVersion.String(), details.StateRequested, "test")
1396+
1397+
mockStdlibFuncs := []common.MockStdLibFuncName{common.CopyFuncName, common.OpenFileFuncName, common.MkdirAllFuncName}
1398+
1399+
testCases := map[string]struct {
1400+
mockStdlibFunc common.MockStdLibFuncName
1401+
mockReturnedError error
1402+
expectedError error
1403+
}{}
1404+
1405+
for _, mockStdlibFunc := range mockStdlibFuncs {
1406+
for _, te := range downloadErrors.OS_DiskSpaceErrors {
1407+
testCases[fmt.Sprintf("unpack_should_return_error_if_unpack_%s_fails: %v", mockStdlibFunc, te)] = struct {
1408+
mockStdlibFunc common.MockStdLibFuncName
1409+
mockReturnedError error
1410+
expectedError error
1411+
}{
1412+
mockStdlibFunc: mockStdlibFunc,
1413+
mockReturnedError: te,
1414+
expectedError: downloadErrors.ErrInsufficientDiskSpace,
1415+
}
1416+
}
1417+
}
1418+
1419+
for name, tc := range testCases {
1420+
t.Run(name, func(t *testing.T) {
1421+
tempDir := t.TempDir()
1422+
paths.SetTop(tempDir)
1423+
paths.SetDownloads(filepath.Join(tempDir, "downloads"))
1424+
1425+
targetArchive, err := createArchive(t, targetArtifactName, targetArchiveFiles)
1426+
require.NoError(t, err)
1427+
1428+
// Used to assert that the mkdirAll error is triggered in the
1429+
// unpacker and not the downloader
1430+
newComponentsDir := filepath.Join(paths.Data(), newVersionedDirName, "components")
1431+
// Used to assert that the openFile error is triggered in the
1432+
// unpacker and not the downloader
1433+
newComponentsFile := filepath.Join(newComponentsDir, "comp1")
1434+
1435+
t.Logf("Created archive: %s", targetArchive)
1436+
1437+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1438+
http.ServeFile(w, r, targetArchive)
1439+
}))
1440+
t.Cleanup(server.Close)
1441+
1442+
config := artifact.Config{
1443+
TargetDirectory: paths.Downloads(),
1444+
SourceURI: server.URL,
1445+
RetrySleepInitDuration: 1 * time.Second,
1446+
HTTPTransportSettings: httpcommon.HTTPTransportSettings{
1447+
Timeout: 1 * time.Second,
1448+
},
1449+
}
1450+
1451+
stdlibMocker := common.PrepareStdLibMocks(common.StdLibMocks{
1452+
CopyMock: func(dst io.Writer, src io.Reader) (int64, error) {
1453+
// If the content is not the same as the target archive components file content,
1454+
// write the content to the destination. This is to make
1455+
// sure that the error is triggered in unpacker and not in downloader
1456+
buf, err := io.ReadAll(src)
1457+
require.NoError(t, err)
1458+
if !bytes.Equal(buf, []byte(targetArchiveComponentsFileContent)) {
1459+
return io.Copy(dst, bytes.NewReader(buf))
1460+
}
1461+
1462+
return 0, tc.mockReturnedError
1463+
},
1464+
OpenFileMock: func(name string, flag int, perm os.FileMode) (*os.File, error) {
1465+
// Make sure that the openFile error is triggered in the
1466+
// unpacker and not the downloader
1467+
if name != newComponentsFile {
1468+
return os.OpenFile(name, flag, perm)
1469+
}
1470+
1471+
return nil, tc.mockReturnedError
1472+
},
1473+
MkdirAllMock: func(path string, perm os.FileMode) error {
1474+
// Make sure that the mkdirAll error is triggered in the
1475+
// unpacker and not the downloader
1476+
if path != newComponentsDir {
1477+
return os.MkdirAll(path, perm)
1478+
}
1479+
1480+
return tc.mockReturnedError
1481+
},
1482+
})
1483+
1484+
stdlibMocker(t, tc.mockStdlibFunc)
1485+
1486+
upgrader, err := NewUpgrader(log, &config, mockAgentInfo)
1487+
require.NoError(t, err)
1488+
1489+
_, err = upgrader.Upgrade(context.Background(), targetVersion.String(), server.URL, nil, upgradeDetails, true, true)
1490+
require.ErrorIs(t, err, tc.expectedError, "expected error mismatch")
1491+
require.Equal(t, upgradeDetails.State, details.StateExtracting)
1492+
})
1493+
}
1494+
}

0 commit comments

Comments
 (0)