-
Notifications
You must be signed in to change notification settings - Fork 5
/
resource.go
143 lines (124 loc) · 4.18 KB
/
resource.go
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package resource
import (
"errors"
"fmt"
"strings"
validation "github.com/go-ozzo/ozzo-validation"
"github.com/go-ozzo/ozzo-validation/is"
"github.com/hashicorp/hcp-sdk-go/clients/cloud-shared/v1/models"
)
const (
tokenOrganization = "organization"
tokenProject = "project"
tokenSep = "/"
)
// Resource is a representation of a HCP resource identifier
type Resource struct {
// ID uniquely identifies a resource within an HCP project
ID string
// Type is the name of the kind of resource identified
Type string
// Organization is the UUID of the HCP organization the resource belongs to
Organization string
// Project is the UUID of the HCP project the resource belongs to
Project string
}
// String encodes the resource identifier in the following canonical format:
//
// "organization/<Organization UUID>/project/<Project UUID>/<Type>/<ID>"
//
// Example:
//
// "organization/ccbdd191-5dc3-4a73-9e05-6ac30ca67992/project/36019e0d-ed59-4df6-9990-05bb7fc793b6/hashicorp.consul.linked-cluster/prod-on-prem"
func (r Resource) String() string {
return strings.Join([]string{
tokenOrganization, r.Organization,
tokenProject, r.Project,
r.Type, r.ID,
}, tokenSep)
}
// Location returns a *models.HashicorpCloudLocationLocation initialized with the Resource's organization and project IDs.
func (r Resource) Location() *models.HashicorpCloudLocationLocation {
return &models.HashicorpCloudLocationLocation{OrganizationID: r.Organization, ProjectID: r.Project}
}
// Link returns a *models.HashicorpCloudLocationLink initialized with values from the Resource
func (r Resource) Link() *models.HashicorpCloudLocationLink {
return &models.HashicorpCloudLocationLink{
ID: r.ID,
Type: r.Type,
Location: r.Location(),
}
}
// FromLink converts a models.HashicorpCloudLocationLink to a Resource.
func FromLink(l *models.HashicorpCloudLocationLink) (r Resource, err error) {
if l == nil || l.Location == nil {
return r, parseErr(errors.New("link and link.Location must not be nil"))
}
return Resource{
ID: l.ID,
Type: l.Type,
Organization: l.Location.OrganizationID,
Project: l.Location.ProjectID,
}, nil
}
// FromString parses the string encoding of a resource identifier.
func FromString(str string) (r Resource, err error) {
err = r.UnmarshalText([]byte(str))
return r, err
}
// UnmarshalText implements the encoding.TextUnmarshaler interface and parses an encoded Resource.
func (r *Resource) UnmarshalText(text []byte) error {
if r == nil {
return fmt.Errorf("resource cannot be nil")
}
parts := strings.SplitN(string(text), tokenSep, 6)
if len(parts) != 6 {
return parseErr(fmt.Errorf("unexpected number of tokens %d", len(parts)))
}
if parts[0] != tokenOrganization {
return parseErr(fmt.Errorf("unexpected token %q", parts[0]))
}
if err := validation.Validate(parts[1], is.UUID); err != nil {
return parseErr(err)
}
if parts[2] != tokenProject {
return parseErr(fmt.Errorf("unexpected token %q", parts[2]))
}
if err := validation.Validate(parts[3], is.UUID); err != nil {
return parseErr(err)
}
r.Organization = parts[1]
r.Project = parts[3]
r.Type = parts[4]
r.ID = parts[5]
return nil
}
func parseErr(err error) error {
return fmt.Errorf("could not parse resource: %w", err)
}
// MarshalText implements the encoding.TextMarshaler interface.
// The encoding is the same as returned by String.
func (r *Resource) MarshalText() ([]byte, error) {
return []byte(r.String()), nil
}
// MarshalJSON implements the json.Marshaler interface
func (r *Resource) MarshalJSON() ([]byte, error) {
return []byte("\"" + r.String() + "\""), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (r *Resource) UnmarshalJSON(bytes []byte) error {
return r.UnmarshalText(bytes[1 : len(bytes)-1])
}
// Must is a helper function that wraps a call to a function returning (Resource, error) such as FromLink or FromString
// and panics if the error is non-nil. It is intended for use in variable
// initializations such as
//
// var packageResource = resource.Must(resource.FromString("..."))
func Must(r Resource, err error) Resource {
if err != nil {
panic(err)
}
return r
}