File: resource_name.go

package info (click to toggle)
golang-github-hashicorp-go-gcp-common 0.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 176 kB
  • sloc: makefile: 2
file content (128 lines) | stat: -rw-r--r-- 3,527 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package gcputil

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"
)

const (
	resourceIdRegex   = "^[^\t\n\f\r]+$"
	collectionIdRegex = "^[a-z][a-zA-Z]*$"

	fullResourceNameRegex = "^//([a-z]+).googleapis.com/(.+)$"
	selfLinkMarker        = "projects/"
)

var singleCollectionIds = map[string]struct{}{
	"global": {},
}

type RelativeResourceName struct {
	Name                 string
	TypeKey              string
	IdTuples             map[string]string
	OrderedCollectionIds []string
}

func ParseRelativeName(resource string) (*RelativeResourceName, error) {
	resourceRe := regexp.MustCompile(resourceIdRegex)
	collectionRe := regexp.MustCompile(collectionIdRegex)

	tokens := strings.Split(resource, "/")
	if len(tokens) < 2 {
		return nil, fmt.Errorf("invalid relative resource name %s (too few tokens)", resource)
	}

	ids := map[string]string{}
	typeKey := ""
	currColId := ""
	for idx, v := range tokens {
		if len(currColId) == 0 {
			if _, ok := singleCollectionIds[v]; ok {
				// Ignore 'single' collectionIds like Global, but error if they are the last ID
				if idx == len(tokens)-1 {
					return nil, fmt.Errorf("invalid relative resource name %s (last collection '%s' has no ID)", resource, currColId)
				}
				continue
			}
			if len(collectionRe.FindAllString(v, 1)) == 0 {
				return nil, fmt.Errorf("invalid relative resource name %s (invalid collection ID %s)", resource, v)
			}
			currColId = v
			typeKey += currColId + "/"
		} else {
			if len(resourceRe.FindAllString(v, 1)) == 0 {
				return nil, fmt.Errorf("invalid relative resource name %s (invalid resource sub-ID %s)", resource, v)
			}
			ids[currColId] = v
			currColId = ""
		}
	}

	typeKey = typeKey[:len(typeKey)-1]
	collectionIds := strings.Split(typeKey, "/")
	resourceName := tokens[len(tokens)-2]
	return &RelativeResourceName{
		Name:                 resourceName,
		TypeKey:              typeKey,
		OrderedCollectionIds: collectionIds,
		IdTuples:             ids,
	}, nil
}

type FullResourceName struct {
	Service string
	*RelativeResourceName
}

func ParseFullResourceName(name string) (*FullResourceName, error) {
	fullRe := regexp.MustCompile(fullResourceNameRegex)
	matches := fullRe.FindAllStringSubmatch(name, 1)
	if len(matches) == 0 {
		return nil, fmt.Errorf("invalid full name '%s'", name)
	}

	if len(matches[0]) != 3 {
		return nil, fmt.Errorf("invalid full name '%s'", name)
	}

	serviceName := matches[0][1]
	relName, err := ParseRelativeName(strings.Trim(matches[0][2], "/"))
	if err != nil {
		return nil, fmt.Errorf("error parsing relative resource path in full resource name '%s': %v", name, err)
	}

	return &FullResourceName{
		Service:              serviceName,
		RelativeResourceName: relName,
	}, nil
}

type SelfLink struct {
	Prefix string
	*RelativeResourceName
}

func ParseProjectResourceSelfLink(link string) (*SelfLink, error) {
	u, err := url.Parse(link)
	if err != nil || u.Scheme == "" || u.Host == "" {
		return nil, fmt.Errorf("invalid self link '%s' must have scheme/host", link)
	}

	split := strings.SplitAfterN(link, selfLinkMarker, 2)
	if len(split) != 2 {
		return nil, fmt.Errorf("self link '%s' is not for project-level resource, must contain '%s')", link, selfLinkMarker)
	}

	relName, err := ParseRelativeName(selfLinkMarker + split[1])
	if err != nil {
		return nil, fmt.Errorf("error parsing relative resource path in self-link '%s': %v", link, err)
	}

	return &SelfLink{
		Prefix:               strings.TrimSuffix(split[0], selfLinkMarker),
		RelativeResourceName: relName,
	}, nil
}