blob: 1d7252d04f517d4e2cd6ad997290186624472d7b [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2016 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package names
5
6import (
7 "fmt"
8 "net/url"
9 "regexp"
10 "strings"
11)
12
13const CloudCredentialTagKind = "cloudcred"
14
15var (
16 cloudCredentialNameSnippet = "[a-zA-Z][a-zA-Z0-9.@_-]*"
17 validCloudCredentialName = regexp.MustCompile("^" + cloudCredentialNameSnippet + "$")
18 validCloudCredential = regexp.MustCompile(
19 "^" +
20 "(" + cloudSnippet + ")" +
21 "/(" + validUserSnippet + ")" + // credential owner
22 "/(" + cloudCredentialNameSnippet + ")" +
23 "$",
24 )
25)
26
27type CloudCredentialTag struct {
28 cloud CloudTag
29 owner UserTag
30 name string
31}
32
33// IsZero reports whether t is zero.
34func (t CloudCredentialTag) IsZero() bool {
35 return t == CloudCredentialTag{}
36}
37
38// Kind is part of the Tag interface.
39func (t CloudCredentialTag) Kind() string { return CloudCredentialTagKind }
40
41// Id implements Tag.Id. It returns the empty string
42// if t is zero.
43func (t CloudCredentialTag) Id() string {
44 if t.IsZero() {
45 return ""
46 }
47 return fmt.Sprintf("%s/%s/%s", t.cloud.Id(), t.owner.Id(), t.name)
48}
49
50func quoteCredentialSeparator(in string) string {
51 return strings.Replace(in, "_", `%5f`, -1)
52}
53
54// String implements Tag.String. It returns the empty
55// string if t is zero.
56func (t CloudCredentialTag) String() string {
57 if t.IsZero() {
58 return ""
59 }
60 return fmt.Sprintf("%s-%s_%s_%s", t.Kind(),
61 quoteCredentialSeparator(t.cloud.Id()),
62 quoteCredentialSeparator(t.owner.Id()),
63 quoteCredentialSeparator(t.name))
64}
65
66// Cloud returns the tag of the cloud to which the credential pertains.
67func (t CloudCredentialTag) Cloud() CloudTag {
68 return t.cloud
69}
70
71// Owner returns the tag of the user that owns the credential.
72func (t CloudCredentialTag) Owner() UserTag {
73 return t.owner
74}
75
76// Name returns the cloud credential name, excluding the
77// cloud and owner qualifiers.
78func (t CloudCredentialTag) Name() string {
79 return t.name
80}
81
82// NewCloudCredentialTag returns the tag for the cloud with the given ID.
83// It will panic if the given cloud ID is not valid.
84func NewCloudCredentialTag(id string) CloudCredentialTag {
85 parts := validCloudCredential.FindStringSubmatch(id)
86 if len(parts) != 4 {
87 panic(fmt.Sprintf("%q is not a valid cloud credential ID", id))
88 }
89 cloud := NewCloudTag(parts[1])
90 owner := NewUserTag(parts[2])
91 return CloudCredentialTag{cloud, owner, parts[3]}
92}
93
94// ParseCloudCredentialTag parses a cloud tag string.
95func ParseCloudCredentialTag(s string) (CloudCredentialTag, error) {
96 tag, err := ParseTag(s)
97 if err != nil {
98 return CloudCredentialTag{}, err
99 }
100 dt, ok := tag.(CloudCredentialTag)
101 if !ok {
102 return CloudCredentialTag{}, invalidTagError(s, CloudCredentialTagKind)
103 }
104 return dt, nil
105}
106
107// IsValidCloudCredential returns whether id is a valid cloud credential ID.
108func IsValidCloudCredential(id string) bool {
109 return validCloudCredential.MatchString(id)
110}
111
112// IsValidCloudCredentialName returns whether name is a valid cloud credential name.
113func IsValidCloudCredentialName(name string) bool {
114 return validCloudCredentialName.MatchString(name)
115}
116
117func cloudCredentialTagSuffixToId(s string) (string, error) {
118 s = strings.Replace(s, "_", "/", -1)
119 return url.QueryUnescape(s)
120}