github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/runtime/store/marker.go (about)

     1  package store
     2  
     3  import (
     4  	"encoding/json"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/ActiveState/cli/internal/constants"
     9  	"github.com/ActiveState/cli/internal/errs"
    10  	"github.com/ActiveState/cli/internal/fileutils"
    11  	"github.com/ActiveState/cli/internal/logging"
    12  	"github.com/go-openapi/strfmt"
    13  )
    14  
    15  type Marker struct {
    16  	CommitID  string `json:"commitID"`
    17  	Namespace string `json:"namespace"`
    18  	Version   string `json:"version"`
    19  }
    20  
    21  func (s *Store) markerFile() string {
    22  	return filepath.Join(s.storagePath, constants.RuntimeInstallationCompleteMarker)
    23  }
    24  
    25  func (s *Store) HasMarker() bool {
    26  	return fileutils.FileExists(s.markerFile())
    27  }
    28  
    29  // MarkerIsValid checks if stored runtime is complete and can be loaded
    30  func (s *Store) MarkerIsValid(commitID strfmt.UUID) bool {
    31  	marker, err := s.parseMarker()
    32  	if err != nil {
    33  		logging.Debug("Unable to parse marker file %s: %v", marker, err)
    34  		return false
    35  	}
    36  
    37  	if marker.CommitID != commitID.String() {
    38  		logging.Debug("Could not match commitID in %s, expected: %s, got: %s", marker, commitID.String(), marker.CommitID)
    39  		return false
    40  	}
    41  
    42  	if marker.Version != constants.Version {
    43  		logging.Debug("Could not match State Tool version in %s, expected: %s, got: %s", marker, constants.Version, marker.Version)
    44  		return false
    45  	}
    46  
    47  	return true
    48  }
    49  
    50  // VersionMarkerIsValid checks if stored runtime was installed with the current state tool version
    51  func (s *Store) VersionMarkerIsValid() bool {
    52  	marker, err := s.parseMarker()
    53  	if err != nil {
    54  		logging.Debug("Unable to parse marker file %s: %v", marker, err)
    55  		return false
    56  	}
    57  
    58  	if marker.Version != constants.Version {
    59  		logging.Debug("Could not match State Tool version in %s, expected: %s, got: %s", marker, constants.Version, marker.Version)
    60  		return false
    61  	}
    62  
    63  	return true
    64  }
    65  
    66  func (s *Store) parseMarker() (*Marker, error) {
    67  	if !s.HasMarker() {
    68  		return nil, errs.New(`Marker file "%s" does not exist`, s.markerFile())
    69  	}
    70  
    71  	contents, err := fileutils.ReadFile(s.markerFile())
    72  	if err != nil {
    73  		return nil, errs.Wrap(err, "Could not read marker file %s", s.markerFile())
    74  	}
    75  
    76  	if !json.Valid(contents) {
    77  		return s.updateMarker(contents)
    78  	}
    79  
    80  	marker := &Marker{}
    81  	err = json.Unmarshal(contents, marker)
    82  	if err != nil {
    83  		return nil, errs.Wrap(err, "Could not unmasrshal marker file")
    84  	}
    85  
    86  	return marker, nil
    87  }
    88  
    89  // updateMarker updates old marker files to the new format and
    90  // returns the stored marker data
    91  func (s *Store) updateMarker(contents []byte) (*Marker, error) {
    92  	lines := strings.Split(string(contents), "\n")
    93  	if len(lines) == 0 {
    94  		// No marker data, nothing to transition
    95  		return nil, nil
    96  	}
    97  
    98  	marker := &Marker{}
    99  	for i, line := range lines {
   100  		if i == 0 {
   101  			marker.CommitID = strings.TrimSpace(line)
   102  		} else if i == 1 {
   103  			marker.Version = strings.TrimSpace(line)
   104  		}
   105  	}
   106  
   107  	data, err := json.Marshal(marker)
   108  	if err != nil {
   109  		return nil, errs.Wrap(err, "Could not marshal marker data")
   110  	}
   111  
   112  	err = fileutils.WriteFile(s.markerFile(), data)
   113  	if err != nil {
   114  		return nil, errs.Wrap(err, "could not set completion marker")
   115  	}
   116  
   117  	return marker, nil
   118  }
   119  
   120  // MarkInstallationComplete writes the installation complete marker to the runtime directory
   121  func (s *Store) MarkInstallationComplete(commitID strfmt.UUID, namespace string) error {
   122  	markerFile := s.markerFile()
   123  	markerDir := filepath.Dir(markerFile)
   124  	err := fileutils.MkdirUnlessExists(markerDir)
   125  	if err != nil {
   126  		return errs.Wrap(err, "could not create completion marker directory")
   127  	}
   128  
   129  	data, err := json.Marshal(Marker{commitID.String(), namespace, constants.Version})
   130  	if err != nil {
   131  		return errs.Wrap(err, "Could not marshal marker data")
   132  	}
   133  
   134  	err = fileutils.WriteFile(markerFile, data)
   135  	if err != nil {
   136  		return errs.Wrap(err, "could not set completion marker")
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  func (s *Store) CommitID() (string, error) {
   143  	marker, err := s.parseMarker()
   144  	if err != nil {
   145  		return "", errs.Wrap(err, "Could not parse marker file")
   146  	}
   147  
   148  	return marker.CommitID, nil
   149  }
   150  
   151  func (s *Store) Namespace() (string, error) {
   152  	marker, err := s.parseMarker()
   153  	if err != nil {
   154  		return "", errs.Wrap(err, "Could not parse marker file")
   155  	}
   156  
   157  	return marker.Namespace, nil
   158  }