126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
|
package client
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// ParseResourceID parses an Azure resource ID
|
||
|
func ParseResourceID(resourceID string) (Resource, error) {
|
||
|
resourceID = strings.Trim(resourceID, "/")
|
||
|
segments := strings.Split(resourceID, "/")
|
||
|
if len(segments)%2 != 0 {
|
||
|
return Resource{}, errors.New("Expected even number of segments")
|
||
|
}
|
||
|
|
||
|
npairs := len(segments) / 2
|
||
|
|
||
|
keys := make([]string, npairs)
|
||
|
values := make([]string, npairs)
|
||
|
for i := 0; i < len(segments); i += 2 {
|
||
|
keys[i/2] = segments[i]
|
||
|
values[i/2] = segments[i+1]
|
||
|
|
||
|
if keys[i/2] == "" {
|
||
|
return Resource{}, fmt.Errorf("Found empty segment (%d)", i)
|
||
|
}
|
||
|
if values[i/2] == "" {
|
||
|
return Resource{}, fmt.Errorf("Found empty segment (%d)", i+1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !strings.EqualFold(keys[0], "subscriptions") {
|
||
|
return Resource{}, fmt.Errorf("Expected first segment to be 'subscriptions', but found %q", keys[0])
|
||
|
}
|
||
|
|
||
|
if !strings.EqualFold(keys[1], "resourceGroups") {
|
||
|
return Resource{}, fmt.Errorf("Expected second segment to be 'resourceGroups', but found %q", keys[1])
|
||
|
}
|
||
|
|
||
|
if !strings.EqualFold(keys[2], "providers") {
|
||
|
return Resource{}, fmt.Errorf("Expected third segment to be 'providers', but found %q", keys[1])
|
||
|
}
|
||
|
|
||
|
r := Resource{
|
||
|
values[0],
|
||
|
values[1],
|
||
|
values[2],
|
||
|
CompoundName(keys[3:]),
|
||
|
CompoundName(values[3:]),
|
||
|
}
|
||
|
if err := r.Validate(); err != nil {
|
||
|
return Resource{}, fmt.Errorf("Error validating resource: %w", err)
|
||
|
}
|
||
|
|
||
|
return r, nil
|
||
|
}
|
||
|
|
||
|
type Resource struct {
|
||
|
Subscription string
|
||
|
ResourceGroup string
|
||
|
Provider string
|
||
|
ResourceType CompoundName
|
||
|
ResourceName CompoundName
|
||
|
}
|
||
|
|
||
|
func (r Resource) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"/subscriptions/%s"+
|
||
|
"/resourceGroups/%s"+
|
||
|
"/providers/%s"+
|
||
|
"/%s",
|
||
|
r.Subscription,
|
||
|
r.ResourceGroup,
|
||
|
r.Provider,
|
||
|
strings.Join(zipstrings(r.ResourceType, r.ResourceName), "/"))
|
||
|
}
|
||
|
|
||
|
func (r Resource) Validate() error {
|
||
|
if r.Subscription == "" {
|
||
|
return errors.New("subscription is not set")
|
||
|
}
|
||
|
if r.ResourceGroup == "" {
|
||
|
return errors.New("resource group is not set")
|
||
|
}
|
||
|
if r.Provider == "" {
|
||
|
return errors.New("provider is not set")
|
||
|
}
|
||
|
if len(r.ResourceType) > len(r.ResourceName) {
|
||
|
return errors.New("not enough values in resource name")
|
||
|
}
|
||
|
if len(r.ResourceType) < len(r.ResourceName) {
|
||
|
return errors.New("too many values in resource name")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Parent produces a resource ID representing the parent resource if this is a child resource
|
||
|
func (r Resource) Parent() (Resource, error) {
|
||
|
newLen := len(r.ResourceType) - 1
|
||
|
if newLen == 0 {
|
||
|
return Resource{}, errors.New("Top-level resource has no parent")
|
||
|
}
|
||
|
return Resource{
|
||
|
Subscription: r.Subscription,
|
||
|
ResourceGroup: r.ResourceGroup,
|
||
|
Provider: r.Provider,
|
||
|
ResourceType: r.ResourceType[:newLen],
|
||
|
ResourceName: r.ResourceName[:newLen],
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
type CompoundName []string
|
||
|
|
||
|
func (n CompoundName) String() string {
|
||
|
return strings.Join(n, "/")
|
||
|
}
|
||
|
|
||
|
func zipstrings(a []string, b []string) []string {
|
||
|
c := make([]string, 0, len(a)+len(b))
|
||
|
for i := 0; i < len(a) && i < len(b); i++ {
|
||
|
c = append(c, a[i], b[i])
|
||
|
}
|
||
|
return c
|
||
|
}
|