Skip to content

Commit

Permalink
share data across input and output adapters via common transfer context
Browse files Browse the repository at this point in the history
Signed-off-by: Vivek Kumar Sahu <[email protected]>
  • Loading branch information
viveksahu26 committed Feb 6, 2025
1 parent 9e3436d commit 51b6b80
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 112 deletions.
10 changes: 5 additions & 5 deletions pkg/adapter/adapter_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
package adapter

import (
"context"
"fmt"

"github.com/interlynk-io/sbommv/pkg/iterator"
"github.com/interlynk-io/sbommv/pkg/logger"
"github.com/interlynk-io/sbommv/pkg/source/github"
"github.com/interlynk-io/sbommv/pkg/target/interlynk"
"github.com/interlynk-io/sbommv/pkg/tcontext"
"github.com/interlynk-io/sbommv/pkg/types"
"github.com/spf13/cobra"
)
Expand All @@ -36,15 +36,15 @@ type Adapter interface {
ParseAndValidateParams(cmd *cobra.Command) error

// Fetch SBOMs lazily using iterator
FetchSBOMs(ctx context.Context) (iterator.SBOMIterator, error)
FetchSBOMs(ctx *tcontext.TransferMetadata) (iterator.SBOMIterator, error)

// Outputs SBOMs (uploading)
UploadSBOMs(ctx context.Context, iterator iterator.SBOMIterator) error
UploadSBOMs(ctx *tcontext.TransferMetadata, iterator iterator.SBOMIterator) error
}

// NewAdapter initializes and returns the correct adapter
func NewAdapter(ctx context.Context, adapterType string, role types.AdapterRole) (Adapter, error) {
logger.LogInfo(ctx, "Initializing adapter", "adapterType", adapterType)
func NewAdapter(ctx *tcontext.TransferMetadata, adapterType string, role types.AdapterRole) (Adapter, error) {
logger.LogInfo(ctx.Context, "Initializing adapter", "adapterType", adapterType)

switch types.AdapterType(adapterType) {

Expand Down
34 changes: 19 additions & 15 deletions pkg/engine/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,69 +28,73 @@ import (
"github.com/interlynk-io/sbommv/pkg/logger"
"github.com/interlynk-io/sbommv/pkg/mvtypes"
"github.com/interlynk-io/sbommv/pkg/sbom"
"github.com/interlynk-io/sbommv/pkg/tcontext"
"github.com/interlynk-io/sbommv/pkg/types"
"github.com/spf13/cobra"
)

func TransferRun(ctx context.Context, cmd *cobra.Command, config mvtypes.Config) error {
logger.LogInfo(ctx, "Starting SBOM transfer process")

// ✅ Initialize shared context with metadata support
transferCtx := tcontext.NewTransferMetadata(ctx)

var inputAdapterInstance adapter.Adapter
var outputAdapterInstance adapter.Adapter
var err error

// Initialize source adapter
inputAdapterInstance, err = adapter.NewAdapter(ctx, config.SourceType, types.AdapterRole("input"))
inputAdapterInstance, err = adapter.NewAdapter(transferCtx, config.SourceType, types.AdapterRole("input"))
if err != nil {
logger.LogError(ctx, err, "Failed to initialize source adapter")
logger.LogError(transferCtx.Context, err, "Failed to initialize source adapter")
return fmt.Errorf("failed to get source adapter: %w", err)
}

// Initialize destination adapter
outputAdapterInstance, err = adapter.NewAdapter(ctx, config.DestinationType, types.AdapterRole("output"))
outputAdapterInstance, err = adapter.NewAdapter(transferCtx, config.DestinationType, types.AdapterRole("output"))
if err != nil {
logger.LogError(ctx, err, "Failed to initialize destination adapter")
logger.LogError(transferCtx.Context, err, "Failed to initialize destination adapter")
return fmt.Errorf("failed to get a destination adapter %v", err)
}

// Parse and validate input adapter parameters
if err := inputAdapterInstance.ParseAndValidateParams(cmd); err != nil {
logger.LogError(ctx, err, "Input adapter error")
logger.LogError(transferCtx.Context, err, "Input adapter error")
return fmt.Errorf("input adapter error: %w", err)

}
logger.LogDebug(ctx, "input adapter instance config", "value", inputAdapterInstance)
logger.LogDebug(transferCtx.Context, "input adapter instance config", "value", inputAdapterInstance)

// Parse and validate output adapter parameters
if err := outputAdapterInstance.ParseAndValidateParams(cmd); err != nil {
return fmt.Errorf("output adapter error: %w", err)
}
logger.LogDebug(ctx, "output adapter instance config", "value", outputAdapterInstance)
logger.LogDebug(transferCtx.Context, "output adapter instance config", "value", outputAdapterInstance)

// Fetch SBOMs lazily using the iterator
logger.LogInfo(ctx, "Fetching SBOMs from input adapter...")
logger.LogInfo(transferCtx.Context, "Fetching SBOMs from input adapter...")

sbomIterator, err := inputAdapterInstance.FetchSBOMs(ctx)
sbomIterator, err := inputAdapterInstance.FetchSBOMs(transferCtx)
if err != nil {
logger.LogError(ctx, err, "Failed to fetch SBOMs")
logger.LogError(transferCtx.Context, err, "Failed to fetch SBOMs")
return fmt.Errorf("failed to fetch SBOMs: %w", err)
}

logger.LogDebug(ctx, "SBOM fetching started successfully", "sbomIterator", sbomIterator)
logger.LogDebug(transferCtx.Context, "SBOM fetching started successfully", "sbomIterator", sbomIterator)

// Dry-Run Mode: Display SBOMs Without Uploading
if config.DryRun {
logger.LogInfo(ctx, "Dry-run mode enabled: Displaying retrieved SBOMs", "values", config.DryRun)
logger.LogInfo(transferCtx.Context, "Dry-run mode enabled: Displaying retrieved SBOMs", "values", config.DryRun)

if err := dryMode(ctx, sbomIterator); err != nil {
if err := dryMode(transferCtx.Context, sbomIterator); err != nil {
return fmt.Errorf("failed to execute dry-run mode: %v", err)
}
return nil
}

// Process & Upload SBOMs Sequentially
if err := outputAdapterInstance.UploadSBOMs(ctx, sbomIterator); err != nil {
logger.LogError(ctx, err, "Failed to output SBOMs")
if err := outputAdapterInstance.UploadSBOMs(transferCtx, sbomIterator); err != nil {
logger.LogError(transferCtx.Context, err, "Failed to output SBOMs")
return fmt.Errorf("failed to output SBOMs: %w", err)
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/source/github/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/interlynk-io/sbommv/pkg/iterator"
"github.com/interlynk-io/sbommv/pkg/logger"
"github.com/interlynk-io/sbommv/pkg/tcontext"
"github.com/interlynk-io/sbommv/pkg/types"
"github.com/interlynk-io/sbommv/pkg/utils"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -112,11 +113,11 @@ func (g *GitHubAdapter) ParseAndValidateParams(cmd *cobra.Command) error {
}

// FetchSBOMs initializes the GitHub SBOM iterator using the unified method
func (g *GitHubAdapter) FetchSBOMs(ctx context.Context) (iterator.SBOMIterator, error) {
func (g *GitHubAdapter) FetchSBOMs(ctx *tcontext.TransferMetadata) (iterator.SBOMIterator, error) {
return NewGitHubIterator(ctx, g)
}

// OutputSBOMs should return an error since GitHub does not support SBOM uploads
func (g *GitHubAdapter) UploadSBOMs(ctx context.Context, iterator iterator.SBOMIterator) error {
func (g *GitHubAdapter) UploadSBOMs(ctx *tcontext.TransferMetadata, iterator iterator.SBOMIterator) error {
return fmt.Errorf("GitHub adapter does not support SBOM uploading")
}
51 changes: 27 additions & 24 deletions pkg/source/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package github

import (
"context"
"encoding/json"
"fmt"
"io"
Expand All @@ -25,6 +24,7 @@ import (
"sync"

"github.com/interlynk-io/sbommv/pkg/logger"
"github.com/interlynk-io/sbommv/pkg/tcontext"
"github.com/schollz/progressbar/v3"
)

Expand Down Expand Up @@ -88,13 +88,13 @@ func NewClient(repoURL, version, method string) *Client {
// FindSBOMs gets all releases assets from github release page
// filter out the particular provided release asset and
// extract SBOMs from that
func (c *Client) FindSBOMs(ctx context.Context) ([]SBOMAsset, error) {
func (c *Client) FindSBOMs(ctx *tcontext.TransferMetadata) ([]SBOMAsset, error) {
owner, repo, err := ParseGitHubURL(c.RepoURL)
if err != nil {
return nil, fmt.Errorf("parsing GitHub URL: %w", err)
}

logger.LogDebug(ctx, "Fetching GitHub releases", "repo_url", c.RepoURL, "owner", owner, "repo", repo)
logger.LogDebug(ctx.Context, "Fetching GitHub releases", "repo_url", c.RepoURL, "owner", owner, "repo", repo)

releases, err := c.GetReleases(ctx, owner, repo)
if err != nil {
Expand All @@ -117,7 +117,7 @@ func (c *Client) FindSBOMs(ctx context.Context) ([]SBOMAsset, error) {
if len(sboms) == 0 {
return nil, fmt.Errorf("no SBOM files found in releases for repository %s/%s", owner, repo)
}
logger.LogDebug(ctx, "Successfully retrieved SBOMs", "total_sboms", len(sboms), "repo_url", c.RepoURL)
logger.LogDebug(ctx.Context, "Successfully retrieved SBOMs", "total_sboms", len(sboms), "repo_url", c.RepoURL)

return sboms, nil
}
Expand Down Expand Up @@ -161,11 +161,11 @@ func (c *Client) extractSBOMs(releases []Release) []SBOMAsset {
}

// GetReleases fetches all releases for a repository
func (c *Client) GetReleases(ctx context.Context, owner, repo string) ([]Release, error) {
func (c *Client) GetReleases(ctx *tcontext.TransferMetadata, owner, repo string) ([]Release, error) {
url := fmt.Sprintf("%s/repos/%s/%s/releases", c.BaseURL, owner, repo)
// logger.LogDebug(ctx, "Fetching GitHub Releases", "url", url)

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
req, err := http.NewRequestWithContext(ctx.Context, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
Expand Down Expand Up @@ -220,8 +220,8 @@ func (c *Client) GetReleases(ctx context.Context, owner, repo string) ([]Release
}

// DownloadAsset downloads a release asset from download url of SBOM
func (c *Client) DownloadAsset(ctx context.Context, downloadURL string) (io.ReadCloser, error) {
req, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil)
func (c *Client) DownloadAsset(ctx *tcontext.TransferMetadata, downloadURL string) (io.ReadCloser, error) {
req, err := http.NewRequestWithContext(ctx.Context, "GET", downloadURL, nil)
if err != nil {
return nil, fmt.Errorf("creating request failed: %w", err)
}
Expand All @@ -240,8 +240,8 @@ func (c *Client) DownloadAsset(ctx context.Context, downloadURL string) (io.Read
}

// FetchSBOMsFromReleases fetches and downloads SBOMs from GitHub releases
func (c *Client) FetchSBOMsFromReleases(ctx context.Context) (map[string][]byte, error) {
logger.LogDebug(ctx, "Fetching SBOMs from GitHub Releases", "repo", c.RepoURL)
func (c *Client) FetchSBOMsFromReleases(ctx *tcontext.TransferMetadata) (map[string][]byte, error) {
logger.LogDebug(ctx.Context, "Fetching SBOMs from GitHub Releases", "repo", c.RepoURL)

// Step 1: Get All Releases
sbomAssets, err := c.FindSBOMs(ctx)
Expand All @@ -253,28 +253,28 @@ func (c *Client) FetchSBOMsFromReleases(ctx context.Context) (map[string][]byte,
return nil, fmt.Errorf("no SBOMs found in repository")
}

logger.LogDebug(ctx, "Total SBOMs found in the repository", "version", c.Version, "total sboms", len(sbomAssets))
logger.LogDebug(ctx.Context, "Total SBOMs found in the repository", "version", c.Version, "total sboms", len(sbomAssets))

// Step 2: Download Each SBOM
versionedSBOMs := make(map[string][]byte)
for _, asset := range sbomAssets {
sbomData, err := c.DownloadSBOM(ctx, asset)
if err != nil {
logger.LogError(ctx, err, "Failed to download SBOM", "file", asset.Name)
logger.LogError(ctx.Context, err, "Failed to download SBOM", "file", asset.Name)
continue
}

versionedSBOMs[asset.Release] = sbomData
logger.LogDebug(ctx, "Downloaded SBOM successfully", "version", asset.Release, "file", asset.Name)
logger.LogDebug(ctx.Context, "Downloaded SBOM successfully", "version", asset.Release, "file", asset.Name)
}

// Step 3: Return All Downloaded SBOMs
return versionedSBOMs, nil
}

// DownloadSBOM fetches an SBOM from its download URL
func (c *Client) DownloadSBOM(ctx context.Context, asset SBOMAsset) ([]byte, error) {
logger.LogDebug(ctx, "Downloading SBOM", "url", asset.DownloadURL)
func (c *Client) DownloadSBOM(ctx *tcontext.TransferMetadata, asset SBOMAsset) ([]byte, error) {
logger.LogDebug(ctx.Context, "Downloading SBOM", "url", asset.DownloadURL)

resp, err := c.httpClient.Get(asset.DownloadURL)
if err != nil {
Expand All @@ -295,7 +295,7 @@ func (c *Client) DownloadSBOM(ctx context.Context, asset SBOMAsset) ([]byte, err
}

// GetSBOMs downloads and saves all SBOM files found in the repository
func (c *Client) GetSBOMs(ctx context.Context, outputDir string) (VersionedSBOMs, error) {
func (c *Client) GetSBOMs(ctx *tcontext.TransferMetadata, outputDir string) (VersionedSBOMs, error) {
// Find SBOMs in releases
sboms, err := c.FindSBOMs(ctx)
if err != nil {
Expand All @@ -305,7 +305,8 @@ func (c *Client) GetSBOMs(ctx context.Context, outputDir string) (VersionedSBOMs
return nil, fmt.Errorf("no SBOMs found in repository")
}

logger.LogDebug(ctx, "Total SBOMs found in the repository", "version", c.Version, "total sboms", len(sboms))
logger.LogDebug(ctx.Context, "Total SBOMs found in the repository", "version", c.Version, "total sboms", len(sboms))
ctx.WithValue("total_sboms", len(sboms))

// Create output directory if needed
if outputDir != "" {
Expand All @@ -318,7 +319,7 @@ func (c *Client) GetSBOMs(ctx context.Context, outputDir string) (VersionedSBOMs
}

// downloadSBOMs handles the concurrent downloading of multiple SBOM files
func (c *Client) downloadSBOMs(ctx context.Context, sboms []SBOMAsset, outputDir string) (VersionedSBOMs, error) {
func (c *Client) downloadSBOMs(ctx *tcontext.TransferMetadata, sboms []SBOMAsset, outputDir string) (VersionedSBOMs, error) {
var (
wg sync.WaitGroup // Coordinates all goroutines
mu sync.Mutex // Protects shared resources
Expand All @@ -327,6 +328,8 @@ func (c *Client) downloadSBOMs(ctx context.Context, sboms []SBOMAsset, outputDir
maxConcurrency = 3 // Maximum parallel downloads
semaphore = make(chan struct{}, maxConcurrency) // Controls concurrency
)
// totalSboms1, _ := ctx.Value("total_sboms").(int)
// fmt.Println("totalSboms: ", totalSboms1)

// Initialize progress bar
bar := progressbar.Default(int64(len(sboms)), "📥 Fetching SBOMs")
Expand Down Expand Up @@ -364,7 +367,7 @@ func (c *Client) downloadSBOMs(ctx context.Context, sboms []SBOMAsset, outputDir
mu.Lock()
versionedSBOMs[sbom.Release] = append(versionedSBOMs[sbom.Release], outputPath)
mu.Unlock()
logger.LogDebug(ctx, "SBOM file", "name", sbom.Name, "saved to", outputPath)
logger.LogDebug(ctx.Context, "SBOM file", "name", sbom.Name, "saved to", outputPath)

// Update progress bar
_ = bar.Add(1)
Expand All @@ -384,7 +387,7 @@ func (c *Client) downloadSBOMs(ctx context.Context, sboms []SBOMAsset, outputDir
}

// downloadSingleSBOM downloads and saves a single SBOM file
func (c *Client) downloadSingleSBOM(ctx context.Context, sbom SBOMAsset, outputPath string) error {
func (c *Client) downloadSingleSBOM(ctx *tcontext.TransferMetadata, sbom SBOMAsset, outputPath string) error {
reader, err := c.DownloadAsset(ctx, sbom.DownloadURL)
if err != nil {
return fmt.Errorf("downloading asset: %w", err)
Expand Down Expand Up @@ -413,18 +416,18 @@ func (c *Client) downloadSingleSBOM(ctx context.Context, sbom SBOMAsset, outputP
return nil
}

func (c *Client) FetchSBOMFromAPI(ctx context.Context) ([]byte, error) {
func (c *Client) FetchSBOMFromAPI(ctx *tcontext.TransferMetadata) ([]byte, error) {
owner, repo, err := ParseGitHubURL(c.RepoURL)
if err != nil {
return nil, fmt.Errorf("parsing GitHub URL: %w", err)
}

// Construct the API URL for the SBOM export
url := fmt.Sprintf("%s/%s", c.BaseURL, fmt.Sprintf(githubSBOMEndpoint, owner, repo))
logger.LogDebug(ctx, "Fetching SBOM via GitHub API", "url", url)
logger.LogDebug(ctx.Context, "Fetching SBOM via GitHub API", "url", url)

// Create request
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
req, err := http.NewRequestWithContext(ctx.Context, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
Expand Down Expand Up @@ -467,7 +470,7 @@ func (c *Client) FetchSBOMFromAPI(ctx context.Context) ([]byte, error) {
return nil, fmt.Errorf("empty SBOM data received from GitHub API")
}

logger.LogDebug(ctx, "Fetched SBOM successfully", "repository", c.RepoURL)
logger.LogDebug(ctx.Context, "Fetched SBOM successfully", "repository", c.RepoURL)

// Return the raw SBOM JSON as bytes
return response.SBOM, nil
Expand Down
Loading

0 comments on commit 51b6b80

Please sign in to comment.