Skip to content

Commit 4678e9e

Browse files
ehl-jfAssaf Attias
andauthored
Xray download indexer api (#1191)
Co-authored-by: Assaf Attias <[email protected]>
1 parent 6d27f59 commit 4678e9e

File tree

5 files changed

+146
-9
lines changed

5 files changed

+146
-9
lines changed

artifactory/services/utils/tests/xray/server.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io"
66
"net/http"
77
"os"
8+
"runtime"
89
"strconv"
910
"strings"
1011
"testing"
@@ -277,6 +278,20 @@ func enrichGetResults(t *testing.T) func(w http.ResponseWriter, r *http.Request)
277278
}
278279
}
279280

281+
func indexerDownloadHandler(t *testing.T) func(w http.ResponseWriter, r *http.Request) {
282+
return func(w http.ResponseWriter, r *http.Request) {
283+
if r.Method == http.MethodGet {
284+
// Simulate downloading a binary file by sending a simple text response
285+
w.Header().Set("Content-Disposition", "attachment; filename=\"indexer\"")
286+
w.Header().Set("Content-Type", "application/octet-stream")
287+
_, err := fmt.Fprint(w, "This is a mock indexer binary content.")
288+
assert.NoError(t, err)
289+
return
290+
}
291+
http.Error(w, "Invalid indexer download request", http.StatusBadRequest)
292+
}
293+
}
294+
280295
type MockServerParams struct {
281296
MSI string
282297
XrayVersion string
@@ -302,6 +317,7 @@ func StartXrayMockServerWithParams(t *testing.T, params MockServerParams) int {
302317
handlers[fmt.Sprintf("/xray/api/v1/scan/graph/%s", params.MSI)] = enrichGetResults(t)
303318
handlers["/xray/api/v1/configuration/jas"] = getJasConfig(t)
304319
handlers[fmt.Sprintf("/%s/", services.BuildScanAPI)] = buildScanHandler
320+
handlers[fmt.Sprintf("/xray/api/v1/indexer-resources/download/%s/%s", runtime.GOOS, runtime.GOARCH)] = indexerDownloadHandler(t)
305321
handlers[fmt.Sprintf("/%s/", services.ReportsAPI)] = reportHandler
306322
// Xsc handlers
307323
handlers["/xsc/api/v1/system/version"] = xscGetVersionHandlerFunc(t, params.XscVersion)

tests/xraybinmgr_test.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ package tests
44

55
import (
66
"fmt"
7+
"slices"
78
"testing"
9+
"time"
810

911
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
1013
)
1114

1215
func TestXrayBinMgr(t *testing.T) {
@@ -16,20 +19,29 @@ func TestXrayBinMgr(t *testing.T) {
1619

1720
func addBuildsToIndexing(t *testing.T) {
1821
buildName := fmt.Sprintf("%s-%s", "build1", getRunId())
19-
defer func() {
20-
assert.NoError(t, deleteBuildIndex(buildName))
21-
assert.NoError(t, deleteBuild(buildName))
22-
}()
22+
t.Cleanup(func() {
23+
if err := deleteBuildIndex(buildName); err != nil {
24+
t.Logf("Failed to delete build index: %v", err)
25+
}
26+
if err := deleteBuild(buildName); err != nil {
27+
t.Logf("Failed to delete build: %v", err)
28+
}
29+
})
2330
// Create a build
2431
err := createDummyBuild(buildName)
25-
assert.NoError(t, err)
32+
require.NoError(t, err)
2633

2734
// Index build
2835
err = testXrayBinMgrService.AddBuildsToIndexing([]string{buildName})
29-
assert.NoError(t, err)
36+
require.NoError(t, err)
3037

3138
// Assert build contained in the indexed build list
32-
indexedBuilds, err := getIndexedBuilds()
33-
assert.NoError(t, err)
34-
assert.Contains(t, indexedBuilds, buildName)
39+
assert.Eventuallyf(t, func() bool {
40+
indexedBuilds, err := getIndexedBuilds()
41+
if err != nil {
42+
t.Logf("Failed to get indexed builds: %v", err)
43+
return false
44+
}
45+
return slices.Contains(indexedBuilds, buildName)
46+
}, time.Second*5, time.Millisecond*150, "Build %s not found in indexed builds", buildName)
3547
}

tests/xrayindexer_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//go:build itest
2+
3+
package tests
4+
5+
import (
6+
"path/filepath"
7+
"strconv"
8+
"testing"
9+
10+
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/jfrog/jfrog-client-go/artifactory/services/utils/tests/xray"
14+
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
15+
"github.com/jfrog/jfrog-client-go/xray/services"
16+
)
17+
18+
func TestXrayDownloadIndexer(t *testing.T) {
19+
initXrayTest(t)
20+
// Create temp dir for downloading the indexer binary
21+
outputDir, err := fileutils.CreateTempDir()
22+
require.NoError(t, err)
23+
defer func() {
24+
require.NoError(t, fileutils.RemoveTempDir(outputDir))
25+
}()
26+
// Create mock Xray server
27+
xrayServerPort := xray.StartXrayMockServer(t)
28+
xrayDetails := GetXrayDetails()
29+
client, err := jfroghttpclient.JfrogClientBuilder().
30+
SetClientCertPath(xrayDetails.GetClientCertPath()).
31+
SetClientCertKeyPath(xrayDetails.GetClientCertKeyPath()).
32+
AppendPreRequestInterceptor(xrayDetails.RunPreRequestFunctions).
33+
Build()
34+
require.NoError(t, err)
35+
// Create indexer service
36+
indexerService := services.NewIndexerService(client)
37+
indexerService.XrayDetails = xrayDetails
38+
indexerService.XrayDetails.SetUrl("http://localhost:" + strconv.Itoa(xrayServerPort) + "/xray/")
39+
// Download the indexer binary
40+
downloadedFilePath, err := indexerService.Download(outputDir, "test-indexer")
41+
require.NoError(t, err)
42+
require.Equal(t, filepath.Join(outputDir, "test-indexer"), downloadedFilePath) // Verify the indexer binary was downloaded successfully
43+
exists, err := fileutils.IsFileExists(downloadedFilePath, false)
44+
require.NoError(t, err)
45+
require.True(t, exists)
46+
}

xray/manager.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,10 @@ func (sm *XrayServicesManager) GetArtifactStatus(repo, path string) (*services.A
269269
artifactService.ScopeProjectKey = sm.scopeProjectKey
270270
return artifactService.GetStatus(repo, path)
271271
}
272+
273+
func (sm *XrayServicesManager) DownloadIndexer(localDirPath, localFileName string) (string, error) {
274+
indexerService := services.NewIndexerService(sm.client)
275+
indexerService.XrayDetails = sm.config.GetServiceDetails()
276+
indexerService.ScopeProjectKey = sm.scopeProjectKey
277+
return indexerService.Download(localDirPath, localFileName)
278+
}

xray/services/indexer.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package services
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"os"
7+
"path/filepath"
8+
"runtime"
9+
10+
"github.com/jfrog/jfrog-client-go/auth"
11+
"github.com/jfrog/jfrog-client-go/http/httpclient"
12+
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
13+
"github.com/jfrog/jfrog-client-go/utils"
14+
"github.com/jfrog/jfrog-client-go/utils/errorutils"
15+
)
16+
17+
const (
18+
downloadIndexerAPI = "api/v1/indexer-resources/download"
19+
)
20+
21+
type IndexerService struct {
22+
client *jfroghttpclient.JfrogHttpClient
23+
XrayDetails auth.ServiceDetails
24+
ScopeProjectKey string
25+
}
26+
27+
func NewIndexerService(client *jfroghttpclient.JfrogHttpClient) *IndexerService {
28+
return &IndexerService{client: client}
29+
}
30+
31+
func (is *IndexerService) Download(localDirPath, localBinaryName string) (string, error) {
32+
httpClientDetails := is.XrayDetails.CreateHttpClientDetails()
33+
url := is.getUrlForDownloadApi()
34+
// Download the indexer from Xray to the provided directory
35+
downloadFileDetails := &httpclient.DownloadFileDetails{DownloadPath: url, LocalPath: localDirPath, LocalFileName: localBinaryName}
36+
resp, err := is.client.DownloadFile(downloadFileDetails, "", &httpClientDetails, false, false)
37+
if err != nil {
38+
return "", fmt.Errorf("failed while attempting to download %q: %w", url, err)
39+
}
40+
if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil {
41+
if resp.StatusCode == http.StatusUnauthorized {
42+
err = fmt.Errorf("%s\nHint: It appears that the credentials provided do not have sufficient permissions for JFrog Xray. This could be due to either incorrect credentials or limited permissions restricted to Artifactory only", err.Error())
43+
}
44+
return "", fmt.Errorf("failed to download %q: %w", url, err)
45+
}
46+
// Add execution permissions to the indexer binary
47+
downloadedFilePath := filepath.Join(localDirPath, localBinaryName)
48+
if err = os.Chmod(downloadedFilePath, 0o755); err != nil {
49+
return "", errorutils.CheckError(err)
50+
}
51+
return downloadedFilePath, errorutils.CheckError(err)
52+
}
53+
54+
func (is *IndexerService) getUrlForDownloadApi() string {
55+
return utils.AppendScopedProjectKeyParam(fmt.Sprintf("%s%s/%s/%s", is.XrayDetails.GetUrl(), downloadIndexerAPI, runtime.GOOS, runtime.GOARCH), is.ScopeProjectKey)
56+
}

0 commit comments

Comments
 (0)