github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/model/checkpoints.go (about)

     1  package model
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/ActiveState/cli/pkg/platform/api/mono/mono_models"
     7  	"github.com/go-openapi/strfmt"
     8  
     9  	"github.com/ActiveState/cli/pkg/sysinfo"
    10  
    11  	"github.com/ActiveState/cli/internal/constants"
    12  	"github.com/ActiveState/cli/internal/errs"
    13  	"github.com/ActiveState/cli/internal/locale"
    14  	"github.com/ActiveState/cli/internal/logging"
    15  	"github.com/ActiveState/cli/pkg/platform/api/graphql"
    16  	gqlModel "github.com/ActiveState/cli/pkg/platform/api/graphql/model"
    17  	"github.com/ActiveState/cli/pkg/platform/api/graphql/request"
    18  	"github.com/ActiveState/cli/pkg/platform/api/inventory/inventory_models"
    19  	"github.com/ActiveState/cli/pkg/platform/authentication"
    20  )
    21  
    22  var (
    23  	ErrNoData = errs.New("no data")
    24  )
    25  
    26  // Checkpoint represents a collection of requirements
    27  type Checkpoint []*mono_models.Checkpoint
    28  
    29  // Language represents a language requirement
    30  type Language struct {
    31  	Name    string `json:"name"`
    32  	Version string `json:"version"`
    33  }
    34  
    35  // GetRequirement searches a commit for a requirement by name.
    36  func GetRequirement(commitID strfmt.UUID, namespace Namespace, requirement string, auth *authentication.Auth) (*gqlModel.Requirement, error) {
    37  	chkPt, _, err := FetchCheckpointForCommit(commitID, auth)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	chkPt = FilterCheckpointNamespace(chkPt, namespace.Type())
    43  
    44  	for _, req := range chkPt {
    45  		if req.Namespace == namespace.String() && req.Requirement == requirement {
    46  			return req, nil
    47  		}
    48  	}
    49  
    50  	return nil, nil
    51  }
    52  
    53  // FetchLanguagesForCommit fetches a list of language names for the given commit
    54  func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]Language, error) {
    55  	checkpoint, _, err := FetchCheckpointForCommit(commitID, auth)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	languages := []Language{}
    61  	for _, requirement := range checkpoint {
    62  		if NamespaceMatch(requirement.Namespace, NamespaceLanguageMatch) {
    63  			version := MonoConstraintsToString(requirement.VersionConstraints)
    64  			lang := Language{
    65  				Name:    requirement.Requirement,
    66  				Version: version,
    67  			}
    68  			languages = append(languages, lang)
    69  		}
    70  	}
    71  
    72  	return languages, nil
    73  }
    74  
    75  // FetchCheckpointForCommit fetches the checkpoint for the given commit
    76  func FetchCheckpointForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]*gqlModel.Requirement, strfmt.DateTime, error) {
    77  	logging.Debug("fetching checkpoint (%s)", commitID.String())
    78  
    79  	request := request.CheckpointByCommit(commitID)
    80  
    81  	gql := graphql.New(auth)
    82  	response := gqlModel.Checkpoint{}
    83  	err := gql.Run(request, &response)
    84  	if err != nil {
    85  		return nil, strfmt.DateTime{}, errs.Wrap(err, "gql.Run failed")
    86  	}
    87  
    88  	logging.Debug("Returning %d requirements", len(response.Requirements))
    89  
    90  	if response.Commit == nil {
    91  		return nil, strfmt.DateTime{}, locale.WrapError(ErrNoData, "err_no_data_found")
    92  	}
    93  
    94  	return response.Requirements, response.Commit.AtTime, nil
    95  }
    96  
    97  func GqlReqsToMonoCheckpoint(requirements []*gqlModel.Requirement) []*mono_models.Checkpoint {
    98  	var result = make([]*mono_models.Checkpoint, 0)
    99  	for _, req := range requirements {
   100  		result = append(result, &req.Checkpoint)
   101  	}
   102  	return result
   103  }
   104  
   105  // FilterCheckpointNamespace filters a Checkpoint removing requirements that do not match the given namespace.
   106  func FilterCheckpointNamespace(chkPt []*gqlModel.Requirement, nsType ...NamespaceType) []*gqlModel.Requirement {
   107  	if chkPt == nil {
   108  		return nil
   109  	}
   110  
   111  	checkpoint := []*gqlModel.Requirement{}
   112  	for _, ns := range nsType {
   113  		for _, requirement := range chkPt {
   114  			if NamespaceMatch(requirement.Namespace, ns.Matchable()) {
   115  				checkpoint = append(checkpoint, requirement)
   116  			}
   117  		}
   118  	}
   119  
   120  	return checkpoint
   121  }
   122  
   123  // CheckpointToRequirements converts a checkpoint to a list of requirements for use with the head-chef
   124  func CheckpointToRequirements(checkpoint Checkpoint) []*inventory_models.OrderRequirement {
   125  	result := []*inventory_models.OrderRequirement{}
   126  
   127  	for _, req := range checkpoint {
   128  		if NamespaceMatch(req.Namespace, NamespacePlatformMatch) {
   129  			continue
   130  		}
   131  		if NamespaceMatch(req.Namespace, NamespaceCamelFlagsMatch) {
   132  			continue
   133  		}
   134  
   135  		result = append(result, &inventory_models.OrderRequirement{
   136  			Feature:             &req.Requirement,
   137  			Namespace:           &req.Namespace,
   138  			VersionRequirements: versionRequirement(req.VersionConstraint),
   139  		})
   140  	}
   141  
   142  	return result
   143  }
   144  
   145  // CheckpointToCamelFlags converts a checkpoint to camel flags
   146  func CheckpointToCamelFlags(checkpoint Checkpoint) []string {
   147  	result := []string{}
   148  
   149  	for _, req := range checkpoint {
   150  		if !NamespaceMatch(req.Namespace, NamespaceCamelFlagsMatch) {
   151  			continue
   152  		}
   153  
   154  		result = append(result, req.Requirement)
   155  	}
   156  
   157  	return result
   158  }
   159  
   160  // versionRequirement returns nil if the version constraint is empty otherwise it will return a valid
   161  // list for a V1OrderRequirements' VersionRequirements. The VersionRequirements can be omitted however
   162  // if it is present then the Version string must be populated with at least one character.
   163  func versionRequirement(versionConstraint string) []*inventory_models.VersionRequirement {
   164  	if versionConstraint == "" {
   165  		return nil
   166  	}
   167  
   168  	var eq = "eq"
   169  	return []*inventory_models.VersionRequirement{{
   170  		Comparator: &eq,
   171  		Version:    &versionConstraint,
   172  	}}
   173  }
   174  
   175  // CheckpointToPlatforms strips platforms from a checkpoint
   176  func CheckpointToPlatforms(requirements []*gqlModel.Requirement) []strfmt.UUID {
   177  	result := []strfmt.UUID{}
   178  
   179  	for _, req := range requirements {
   180  		if !NamespaceMatch(req.Namespace, NamespacePlatformMatch) {
   181  			continue
   182  		}
   183  		result = append(result, strfmt.UUID(req.Requirement))
   184  	}
   185  
   186  	return result
   187  }
   188  
   189  // CheckpointToLanguage returns the language from a checkpoint
   190  func CheckpointToLanguage(requirements []*gqlModel.Requirement, auth *authentication.Auth) (*Language, error) {
   191  	for _, req := range requirements {
   192  		if !NamespaceMatch(req.Namespace, NamespaceLanguageMatch) {
   193  			continue
   194  		}
   195  		lang, err := FetchLanguageByDetails(req.Requirement, req.VersionConstraint, auth)
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  		return lang, nil
   200  	}
   201  
   202  	return nil, locale.NewError("err_fetch_languages")
   203  }
   204  
   205  func PlatformNameToPlatformID(name string) (string, error) {
   206  	name = strings.ToLower(name)
   207  	if name == "darwin" {
   208  		name = "macos"
   209  	}
   210  	id, err := hostPlatformToPlatformID(name)
   211  	return id, err
   212  }
   213  
   214  func hostPlatformToPlatformID(os string) (string, error) {
   215  	switch strings.ToLower(os) {
   216  	case strings.ToLower(sysinfo.Linux.String()):
   217  		return constants.LinuxBit64UUID, nil
   218  	case strings.ToLower(sysinfo.Mac.String()):
   219  		return constants.MacBit64UUID, nil
   220  	case strings.ToLower(sysinfo.Windows.String()):
   221  		return constants.Win10Bit64UUID, nil
   222  	default:
   223  		return "", locale.NewExternalError("err_unsupported_platform", "", os)
   224  	}
   225  }
   226  
   227  func HostPlatformToKernelName(os string) string {
   228  	switch strings.ToLower(os) {
   229  	case strings.ToLower(sysinfo.Linux.String()):
   230  		return "Linux"
   231  	case strings.ToLower(sysinfo.Mac.String()):
   232  		return "Darwin"
   233  	case strings.ToLower(sysinfo.Windows.String()):
   234  		return "Windows"
   235  	default:
   236  		return ""
   237  	}
   238  }
   239  
   240  func platformArchToHostArch(arch, bits string) string {
   241  	switch bits {
   242  	case "32":
   243  		switch arch {
   244  		case "IA64":
   245  			return "nonexistent"
   246  		case "PA-RISC":
   247  			return "unsupported"
   248  		case "PowerPC":
   249  			return "ppc"
   250  		case "Sparc":
   251  			return "sparc"
   252  		case "x86":
   253  			return "386"
   254  		}
   255  	case "64":
   256  		switch arch {
   257  		case "IA64":
   258  			return "unsupported"
   259  		case "PA-RISC":
   260  			return "unsupported"
   261  		case "PowerPC":
   262  			return "ppc64"
   263  		case "Sparc":
   264  			return "sparc64"
   265  		case "x86":
   266  			return "amd64"
   267  		}
   268  	}
   269  	return "unrecognized"
   270  }
   271  
   272  func fallbackArch(platform, arch string) string {
   273  	// On the M1 Mac platform we default to
   274  	// amd64 as the platform does not support arm.
   275  	if arch == "arm64" && platform == sysinfo.Mac.String() {
   276  		return "amd64"
   277  	}
   278  	return arch
   279  }