github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/transferfiles/state/runstatus.go (about)

     1  package state
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
    10  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    11  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    12  )
    13  
    14  // The current version of the run-status.json file.
    15  // Can be used to identify when the version of the CLI doesn't support the structure of the transfer directory.
    16  const transferRunStatusVersion = 1
    17  
    18  var saveRunStatusMutex sync.Mutex
    19  
    20  type ActionOnStatusFunc func(transferRunStatus *TransferRunStatus) error
    21  
    22  // This struct holds the run status of the current transfer.
    23  // It is saved to a file in JFrog CLI's home, but gets reset every time the transfer begins.
    24  // This state is used to allow showing the current run status by the 'jf rt transfer-files --status' command.
    25  // It is also used for the time estimation and more.
    26  type TransferRunStatus struct {
    27  	// Timestamp since the beginning of the current transfer execution
    28  	startTimestamp    time.Time
    29  	lastSaveTimestamp time.Time
    30  	// This variable holds the total/transferred number of repositories (not their files).
    31  	OverallTransfer   ProgressState      `json:"overall_transfer,omitempty"`
    32  	TotalRepositories ProgressStateUnits `json:"total_repositories,omitempty"`
    33  	OverallBiFiles    ProgressStateUnits `json:"overall_bi_files,omitempty"`
    34  	// Version of the TransferRunStatus file.
    35  	Version        int    `json:"version,omitempty"`
    36  	CurrentRepoKey string `json:"current_repo,omitempty"`
    37  	// True if currently transferring a build info repository.
    38  	BuildInfoRepo         bool   `json:"build_info_repo,omitempty"`
    39  	CurrentRepoPhase      int    `json:"current_repo_phase,omitempty"`
    40  	WorkingThreads        int    `json:"working_threads,omitempty"`
    41  	VisitedFolders        uint64 `json:"visited_folders,omitempty"`
    42  	DelayedFiles          uint64 `json:"delayed_files,omitempty"`
    43  	TransferFailures      uint64 `json:"transfer_failures,omitempty"`
    44  	TimeEstimationManager `json:"time_estimation,omitempty"`
    45  	StaleChunks           []StaleChunks `json:"stale_chunks,omitempty"`
    46  }
    47  
    48  // This structure contains a collection of chunks that have been undergoing processing for over 30 minutes
    49  type StaleChunks struct {
    50  	NodeID string       `json:"node_id,omitempty"`
    51  	Chunks []StaleChunk `json:"stale_node_chunks,omitempty"`
    52  }
    53  
    54  type StaleChunk struct {
    55  	ChunkID string   `json:"chunk_id,omitempty"`
    56  	Files   []string `json:"files,omitempty"`
    57  	Sent    int64    `json:"sent,omitempty"`
    58  }
    59  
    60  func (ts *TransferRunStatus) action(action ActionOnStatusFunc) error {
    61  	if err := action(ts); err != nil {
    62  		return err
    63  	}
    64  
    65  	now := time.Now()
    66  	if now.Sub(ts.lastSaveTimestamp).Seconds() < float64(stateAndStatusSaveIntervalSecs) {
    67  		return nil
    68  	}
    69  
    70  	if !saveRunStatusMutex.TryLock() {
    71  		return nil
    72  	}
    73  	defer saveRunStatusMutex.Unlock()
    74  
    75  	ts.lastSaveTimestamp = now
    76  	return ts.persistTransferRunStatus()
    77  }
    78  
    79  func (ts *TransferRunStatus) persistTransferRunStatus() (err error) {
    80  	statusFilePath, err := coreutils.GetJfrogTransferRunStatusFilePath()
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	ts.Version = transferRunStatusVersion
    86  	content, err := json.Marshal(ts)
    87  	if err != nil {
    88  		return errorutils.CheckError(err)
    89  	}
    90  
    91  	err = os.WriteFile(statusFilePath, content, 0600)
    92  	if err != nil {
    93  		return errorutils.CheckError(err)
    94  	}
    95  	return nil
    96  }
    97  
    98  func loadTransferRunStatus() (transferRunStatus TransferRunStatus, exists bool, err error) {
    99  	statusFilePath, err := coreutils.GetJfrogTransferRunStatusFilePath()
   100  	if err != nil {
   101  		return
   102  	}
   103  	exists, err = fileutils.IsFileExists(statusFilePath, false)
   104  	if err != nil || !exists {
   105  		return
   106  	}
   107  
   108  	content, err := fileutils.ReadFile(statusFilePath)
   109  	if err != nil {
   110  		return
   111  	}
   112  
   113  	err = errorutils.CheckError(json.Unmarshal(content, &transferRunStatus))
   114  	return
   115  }
   116  
   117  func VerifyTransferRunStatusVersion() error {
   118  	transferRunStatus, exists, err := loadTransferRunStatus()
   119  	if err != nil || !exists {
   120  		return err
   121  	}
   122  	if transferRunStatus.Version != transferRunStatusVersion {
   123  		return GetOldTransferDirectoryStructureError()
   124  	}
   125  	return nil
   126  }