go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/mrn/mrn.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package mrn
     5  
     6  import (
     7  	"errors"
     8  	"net/url"
     9  	"path"
    10  	"regexp"
    11  	"strings"
    12  )
    13  
    14  func ServiceID(serviceName string, baseDomain string) string {
    15  	res := strings.TrimSuffix(serviceName, baseDomain)
    16  	return res
    17  }
    18  
    19  func IsValid(mrn string) bool {
    20  	x, err := url.Parse(mrn)
    21  	if err != nil {
    22  		return false
    23  	}
    24  
    25  	return x.Scheme == "" && x.Fragment == "" && x.RawQuery == ""
    26  }
    27  
    28  func NewMRN(fullResourceName string) (*MRN, error) {
    29  	u, err := url.Parse(fullResourceName)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	path := strings.TrimLeft(u.EscapedPath(), "/")
    35  
    36  	return &MRN{
    37  		ServiceName:          u.Host,
    38  		RelativeResourceName: path,
    39  	}, nil
    40  }
    41  
    42  func NewChildMRN(ownerMRN string, resource string, resourceID string) (*MRN, error) {
    43  	if !isValidResourceID(resourceID) {
    44  		return nil, errors.New("invalid " + resource + " ID: " + resourceID)
    45  	}
    46  
    47  	mrn, err := NewMRN(ownerMRN)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	mrn.RelativeResourceName = path.Join(mrn.RelativeResourceName, resource, resourceID)
    53  	return mrn, nil
    54  }
    55  
    56  func GetResource(mrn string, resource string) (string, error) {
    57  	parsed, err := NewMRN(mrn)
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  
    62  	res, err := parsed.ResourceID(resource)
    63  	if err != nil {
    64  		return "", errors.New("invalid " + resource + " in mrn: " + mrn)
    65  	}
    66  	return res, nil
    67  }
    68  
    69  // SafeComponentString sanitizes a string so that it can be safely used as a uri component for mrns
    70  func SafeComponentString(s string) string {
    71  	if s == "" {
    72  		return s
    73  	}
    74  	s = strings.ReplaceAll(s, " ", "_")
    75  	s = strings.ReplaceAll(s, "/", "-")
    76  	return s
    77  }
    78  
    79  // ID: lowercase letters, digits, dots or hyphens, fewer than 200 chars, more than 4 chars.
    80  // They may include dots/hyphens etc, e.g. 1.1.2-tmp-configured
    81  var reResourceID = regexp.MustCompile(`^([\d-_\.]|[a-zA-Z]){4,200}$`)
    82  
    83  func isValidResourceID(id string) bool {
    84  	return reResourceID.MatchString(id)
    85  }
    86  
    87  // MRN follows Google's Design for resource names
    88  // see https://cloud.google.com/apis/design/resource_names
    89  type MRN struct {
    90  	ServiceName          string
    91  	RelativeResourceName string
    92  }
    93  
    94  func (mrn *MRN) String() string {
    95  	return "//" + mrn.ServiceName + "/" + mrn.RelativeResourceName
    96  }
    97  
    98  func (mrn *MRN) Basename() string {
    99  	keyValues := strings.Split(mrn.RelativeResourceName, "/")
   100  	if len(keyValues) == 0 {
   101  		return ""
   102  	}
   103  	return keyValues[len(keyValues)-1]
   104  }
   105  
   106  func (mrn *MRN) ResourceID(collectionId string) (string, error) {
   107  	keyValues := strings.Split(mrn.RelativeResourceName, "/")
   108  	for i := 0; i < len(keyValues); {
   109  		if keyValues[i] == collectionId {
   110  			if i+1 < len(keyValues) {
   111  				return keyValues[i+1], nil
   112  			} else {
   113  				return "", errors.New("invalid mrn collection id scheme: " + mrn.String())
   114  			}
   115  		}
   116  		i = i + 2
   117  	}
   118  	return "", errors.New("could not find collection id: " + collectionId)
   119  }
   120  
   121  func (mrn *MRN) Equals(resource string) bool {
   122  	parsed, err := NewMRN(resource)
   123  	if err != nil {
   124  		return false
   125  	}
   126  	if parsed.ServiceName == mrn.ServiceName && parsed.RelativeResourceName == mrn.RelativeResourceName {
   127  		return true
   128  	}
   129  	return false
   130  }