packer-cn/builder/azure/common/client/resource.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
}