cord-776 create build / runtime containers for autmation uservices
Change-Id: I246973192adef56a250ffe93a5f65fff488840c1
diff --git a/automation/vendor/gopkg.in/juju/names.v2/LICENSE b/automation/vendor/gopkg.in/juju/names.v2/LICENSE
new file mode 100644
index 0000000..ade9307
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/LICENSE
@@ -0,0 +1,191 @@
+All files in this repository are licensed as follows. If you contribute
+to this repository, it is assumed that you license your contribution
+under the same license unless you state otherwise.
+
+All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
+
+This software is licensed under the LGPLv3, included below.
+
+As a special exception to the GNU Lesser General Public License version 3
+("LGPL3"), the copyright holders of this Library give you permission to
+convey to a third party a Combined Work that links statically or dynamically
+to this Library without providing any Minimal Corresponding Source or
+Minimal Application Code as set out in 4d or providing the installation
+information set out in section 4e, provided that you comply with the other
+provisions of LGPL3 and provided that you meet, for the Application the
+terms and conditions of the license(s) which apply to the Application.
+
+Except as stated in this special exception, the provisions of LGPL3 will
+continue to comply in full to this Library. If you modify this Library, you
+may apply this exception to your version of this Library, but you are not
+obliged to do so. If you do not wish to do so, delete this exception
+statement from your version. This exception does not (and cannot) modify any
+license terms which apply to the Application, with which you must still
+comply.
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/automation/vendor/gopkg.in/juju/names.v2/README.md b/automation/vendor/gopkg.in/juju/names.v2/README.md
new file mode 100644
index 0000000..ecacdc7
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/README.md
@@ -0,0 +1,4 @@
+juju/names
+============
+
+This package provides helpers for handling Juju entity names.
diff --git a/automation/vendor/gopkg.in/juju/names.v2/action.go b/automation/vendor/gopkg.in/juju/names.v2/action.go
new file mode 100644
index 0000000..3211881
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/action.go
@@ -0,0 +1,79 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+
+ "github.com/juju/errors"
+ "github.com/juju/utils"
+)
+
+const ActionTagKind = "action"
+
+type ActionTag struct {
+ // Tags that are serialized need to have fields exported.
+ ID utils.UUID
+}
+
+// NewActionTag returns the tag of an action with the given id (UUID).
+func NewActionTag(id string) ActionTag {
+ uuid, err := utils.UUIDFromString(id)
+ if err != nil {
+ panic(err)
+ }
+ return ActionTag{ID: uuid}
+}
+
+// ParseActionTag parses an action tag string.
+func ParseActionTag(actionTag string) (ActionTag, error) {
+ tag, err := ParseTag(actionTag)
+ if err != nil {
+ return ActionTag{}, err
+ }
+ at, ok := tag.(ActionTag)
+ if !ok {
+ return ActionTag{}, invalidTagError(actionTag, ActionTagKind)
+ }
+ return at, nil
+}
+
+func (t ActionTag) String() string { return t.Kind() + "-" + t.Id() }
+func (t ActionTag) Kind() string { return ActionTagKind }
+func (t ActionTag) Id() string { return t.ID.String() }
+
+// IsValidAction returns whether id is a valid action id (UUID).
+func IsValidAction(id string) bool {
+ return utils.IsValidUUIDString(id)
+}
+
+// ActionReceiverTag returns an ActionReceiver Tag from a
+// machine or unit name.
+func ActionReceiverTag(name string) (Tag, error) {
+ if IsValidUnit(name) {
+ return NewUnitTag(name), nil
+ }
+ if IsValidApplication(name) {
+ // TODO(jcw4) enable when leader elections complete
+ //return NewApplicationTag(name), nil
+ }
+ if IsValidMachine(name) {
+ return NewMachineTag(name), nil
+ }
+ return nil, fmt.Errorf("invalid actionreceiver name %q", name)
+}
+
+// ActionReceiverFrom Tag returns an ActionReceiver tag from
+// a machine or unit tag.
+func ActionReceiverFromTag(tag string) (Tag, error) {
+ unitTag, err := ParseUnitTag(tag)
+ if err == nil {
+ return unitTag, nil
+ }
+ machineTag, err := ParseMachineTag(tag)
+ if err == nil {
+ return machineTag, nil
+ }
+ return nil, errors.Errorf("invalid actionreceiver tag %q", tag)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/application.go b/automation/vendor/gopkg.in/juju/names.v2/application.go
new file mode 100644
index 0000000..aa8985e
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/application.go
@@ -0,0 +1,48 @@
+// Copyright 2013 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "regexp"
+)
+
+const ApplicationTagKind = "application"
+
+const (
+ ApplicationSnippet = "(?:[a-z][a-z0-9]*(?:-[a-z0-9]*[a-z][a-z0-9]*)*)"
+ NumberSnippet = "(?:0|[1-9][0-9]*)"
+)
+
+var validApplication = regexp.MustCompile("^" + ApplicationSnippet + "$")
+
+// IsValidApplication returns whether name is a valid application name.
+func IsValidApplication(name string) bool {
+ return validApplication.MatchString(name)
+}
+
+type ApplicationTag struct {
+ Name string
+}
+
+func (t ApplicationTag) String() string { return t.Kind() + "-" + t.Id() }
+func (t ApplicationTag) Kind() string { return ApplicationTagKind }
+func (t ApplicationTag) Id() string { return t.Name }
+
+// NewApplicationTag returns the tag for the application with the given name.
+func NewApplicationTag(applicationName string) ApplicationTag {
+ return ApplicationTag{Name: applicationName}
+}
+
+// ParseApplicationTag parses a application tag string.
+func ParseApplicationTag(applicationTag string) (ApplicationTag, error) {
+ tag, err := ParseTag(applicationTag)
+ if err != nil {
+ return ApplicationTag{}, err
+ }
+ st, ok := tag.(ApplicationTag)
+ if !ok {
+ return ApplicationTag{}, invalidTagError(applicationTag, ApplicationTagKind)
+ }
+ return st, nil
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/charm.go b/automation/vendor/gopkg.in/juju/names.v2/charm.go
new file mode 100644
index 0000000..3984426
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/charm.go
@@ -0,0 +1,105 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+)
+
+// CharmTagKind specifies charm tag kind
+const CharmTagKind = "charm"
+
+// Valid charm url can be either in V1 or V3 format. (V2 is a
+// charmstore web URL like https://jujucharms.com/postgresql/105, but
+// that's not valid as a tag.)
+//
+// V1 is of the form:
+// schema:~user/series/name-revision
+// where
+// schema is optional and can be either "local" or "cs".
+// When not supplied, "cs" is implied.
+// user is optional and is only applicable for "cs" schema
+// series is optional and is a valid series name
+// name is mandatory and is the name of the charm
+// revision is optional and can be -1 if revision is unset
+//
+// V3 is of the form
+// schema:user/name/series/revision
+// with the same fields and constraints as the V1 format.
+
+var (
+ // SeriesSnippet is a regular expression representing series
+ SeriesSnippet = "[a-z]+([a-z0-9]+)?"
+
+ // CharmNameSnippet is a regular expression representing charm name
+ CharmNameSnippet = "[a-z][a-z0-9]*(-[a-z0-9]*[a-z][a-z0-9]*)*"
+
+ localSchemaSnippet = "local:"
+ v1CharmStoreSchemaSnippet = "cs:(~" + validUserNameSnippet + "/)?"
+ revisionSnippet = "(-1|0|[1-9][0-9]*)"
+
+ validV1CharmRegEx = regexp.MustCompile("^(" +
+ localSchemaSnippet + "|" +
+ v1CharmStoreSchemaSnippet + ")?(" +
+ SeriesSnippet + "/)?" +
+ CharmNameSnippet + "(-" +
+ revisionSnippet + ")?$")
+
+ v3CharmStoreSchemaSnippet = "(cs:)?(" + validUserNameSnippet + "/)?"
+
+ validV3CharmRegEx = regexp.MustCompile("^(" +
+ localSchemaSnippet + "|" +
+ v3CharmStoreSchemaSnippet + ")" +
+ CharmNameSnippet + "(/" +
+ SeriesSnippet + ")?(/" +
+ revisionSnippet + ")?$")
+)
+
+// CharmTag represents tag for charm
+// using charm's URL
+type CharmTag struct {
+ url string
+}
+
+// String satisfies Tag interface.
+// Produces string representation of charm tag.
+func (t CharmTag) String() string { return t.Kind() + "-" + t.Id() }
+
+// Kind satisfies Tag interface.
+// Returns Charm tag kind.
+func (t CharmTag) Kind() string { return CharmTagKind }
+
+// Id satisfies Tag interface.
+// Returns charm URL.
+func (t CharmTag) Id() string { return t.url }
+
+// NewCharmTag returns the tag for the charm with the given url.
+// It will panic if the given charm url is not valid.
+func NewCharmTag(charmURL string) CharmTag {
+ if !IsValidCharm(charmURL) {
+ panic(fmt.Sprintf("%q is not a valid charm name", charmURL))
+ }
+ return CharmTag{url: charmURL}
+}
+
+var emptyTag = CharmTag{}
+
+// ParseCharmTag parses a charm tag string.
+func ParseCharmTag(charmTag string) (CharmTag, error) {
+ tag, err := ParseTag(charmTag)
+ if err != nil {
+ return emptyTag, err
+ }
+ ct, ok := tag.(CharmTag)
+ if !ok {
+ return emptyTag, invalidTagError(charmTag, CharmTagKind)
+ }
+ return ct, nil
+}
+
+// IsValidCharm returns whether name is a valid charm url.
+func IsValidCharm(url string) bool {
+ return validV1CharmRegEx.MatchString(url) || validV3CharmRegEx.MatchString(url)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/cloud.go b/automation/vendor/gopkg.in/juju/names.v2/cloud.go
new file mode 100644
index 0000000..78a3c8b
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/cloud.go
@@ -0,0 +1,51 @@
+// Copyright 2016 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+)
+
+const CloudTagKind = "cloud"
+
+var (
+ cloudSnippet = "[a-zA-Z0-9][a-zA-Z0-9.-]*"
+ validCloud = regexp.MustCompile("^" + cloudSnippet + "$")
+)
+
+type CloudTag struct {
+ id string
+}
+
+func (t CloudTag) String() string { return t.Kind() + "-" + t.id }
+func (t CloudTag) Kind() string { return CloudTagKind }
+func (t CloudTag) Id() string { return t.id }
+
+// NewCloudTag returns the tag for the cloud with the given ID.
+// It will panic if the given cloud ID is not valid.
+func NewCloudTag(id string) CloudTag {
+ if !IsValidCloud(id) {
+ panic(fmt.Sprintf("%q is not a valid cloud ID", id))
+ }
+ return CloudTag{id}
+}
+
+// ParseCloudTag parses a cloud tag string.
+func ParseCloudTag(cloudTag string) (CloudTag, error) {
+ tag, err := ParseTag(cloudTag)
+ if err != nil {
+ return CloudTag{}, err
+ }
+ dt, ok := tag.(CloudTag)
+ if !ok {
+ return CloudTag{}, invalidTagError(cloudTag, CloudTagKind)
+ }
+ return dt, nil
+}
+
+// IsValidCloud returns whether id is a valid cloud ID.
+func IsValidCloud(id string) bool {
+ return validCloud.MatchString(id)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/cloudcredential.go b/automation/vendor/gopkg.in/juju/names.v2/cloudcredential.go
new file mode 100644
index 0000000..1d7252d
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/cloudcredential.go
@@ -0,0 +1,120 @@
+// Copyright 2016 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "net/url"
+ "regexp"
+ "strings"
+)
+
+const CloudCredentialTagKind = "cloudcred"
+
+var (
+ cloudCredentialNameSnippet = "[a-zA-Z][a-zA-Z0-9.@_-]*"
+ validCloudCredentialName = regexp.MustCompile("^" + cloudCredentialNameSnippet + "$")
+ validCloudCredential = regexp.MustCompile(
+ "^" +
+ "(" + cloudSnippet + ")" +
+ "/(" + validUserSnippet + ")" + // credential owner
+ "/(" + cloudCredentialNameSnippet + ")" +
+ "$",
+ )
+)
+
+type CloudCredentialTag struct {
+ cloud CloudTag
+ owner UserTag
+ name string
+}
+
+// IsZero reports whether t is zero.
+func (t CloudCredentialTag) IsZero() bool {
+ return t == CloudCredentialTag{}
+}
+
+// Kind is part of the Tag interface.
+func (t CloudCredentialTag) Kind() string { return CloudCredentialTagKind }
+
+// Id implements Tag.Id. It returns the empty string
+// if t is zero.
+func (t CloudCredentialTag) Id() string {
+ if t.IsZero() {
+ return ""
+ }
+ return fmt.Sprintf("%s/%s/%s", t.cloud.Id(), t.owner.Id(), t.name)
+}
+
+func quoteCredentialSeparator(in string) string {
+ return strings.Replace(in, "_", `%5f`, -1)
+}
+
+// String implements Tag.String. It returns the empty
+// string if t is zero.
+func (t CloudCredentialTag) String() string {
+ if t.IsZero() {
+ return ""
+ }
+ return fmt.Sprintf("%s-%s_%s_%s", t.Kind(),
+ quoteCredentialSeparator(t.cloud.Id()),
+ quoteCredentialSeparator(t.owner.Id()),
+ quoteCredentialSeparator(t.name))
+}
+
+// Cloud returns the tag of the cloud to which the credential pertains.
+func (t CloudCredentialTag) Cloud() CloudTag {
+ return t.cloud
+}
+
+// Owner returns the tag of the user that owns the credential.
+func (t CloudCredentialTag) Owner() UserTag {
+ return t.owner
+}
+
+// Name returns the cloud credential name, excluding the
+// cloud and owner qualifiers.
+func (t CloudCredentialTag) Name() string {
+ return t.name
+}
+
+// NewCloudCredentialTag returns the tag for the cloud with the given ID.
+// It will panic if the given cloud ID is not valid.
+func NewCloudCredentialTag(id string) CloudCredentialTag {
+ parts := validCloudCredential.FindStringSubmatch(id)
+ if len(parts) != 4 {
+ panic(fmt.Sprintf("%q is not a valid cloud credential ID", id))
+ }
+ cloud := NewCloudTag(parts[1])
+ owner := NewUserTag(parts[2])
+ return CloudCredentialTag{cloud, owner, parts[3]}
+}
+
+// ParseCloudCredentialTag parses a cloud tag string.
+func ParseCloudCredentialTag(s string) (CloudCredentialTag, error) {
+ tag, err := ParseTag(s)
+ if err != nil {
+ return CloudCredentialTag{}, err
+ }
+ dt, ok := tag.(CloudCredentialTag)
+ if !ok {
+ return CloudCredentialTag{}, invalidTagError(s, CloudCredentialTagKind)
+ }
+ return dt, nil
+}
+
+// IsValidCloudCredential returns whether id is a valid cloud credential ID.
+func IsValidCloudCredential(id string) bool {
+ return validCloudCredential.MatchString(id)
+}
+
+// IsValidCloudCredentialName returns whether name is a valid cloud credential name.
+func IsValidCloudCredentialName(name string) bool {
+ return validCloudCredentialName.MatchString(name)
+}
+
+func cloudCredentialTagSuffixToId(s string) (string, error) {
+ s = strings.Replace(s, "_", "/", -1)
+ return url.QueryUnescape(s)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/controller.go b/automation/vendor/gopkg.in/juju/names.v2/controller.go
new file mode 100644
index 0000000..c48c326
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/controller.go
@@ -0,0 +1,56 @@
+// Copyright 2016 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "regexp"
+)
+
+// ControllerTagKind indicates that a tag belongs to a controller.
+const ControllerTagKind = "controller"
+
+// ControllerTag represents a tag used to describe a controller.
+type ControllerTag struct {
+ uuid string
+}
+
+// Lowercase letters, digits and (non-leading) hyphens.
+var validControllerName = regexp.MustCompile(`^[a-z0-9]+[a-z0-9-]*$`)
+
+// NewControllerTag returns the tag of an controller with the given controller UUID.
+func NewControllerTag(uuid string) ControllerTag {
+ return ControllerTag{uuid: uuid}
+}
+
+// ParseControllerTag parses an environ tag string.
+func ParseControllerTag(controllerTag string) (ControllerTag, error) {
+ tag, err := ParseTag(controllerTag)
+ if err != nil {
+ return ControllerTag{}, err
+ }
+ et, ok := tag.(ControllerTag)
+ if !ok {
+ return ControllerTag{}, invalidTagError(controllerTag, ControllerTagKind)
+ }
+ return et, nil
+}
+
+// String implements Tag.
+func (t ControllerTag) String() string { return t.Kind() + "-" + t.Id() }
+
+// Kind implements Tag.
+func (t ControllerTag) Kind() string { return ControllerTagKind }
+
+// Id implements Tag.
+func (t ControllerTag) Id() string { return t.uuid }
+
+// IsValidController returns whether id is a valid controller UUID.
+func IsValidController(id string) bool {
+ return validUUID.MatchString(id)
+}
+
+// IsValidControllerName returns whether name is a valid string safe for a controller name.
+func IsValidControllerName(name string) bool {
+ return validControllerName.MatchString(name)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/environ.go b/automation/vendor/gopkg.in/juju/names.v2/environ.go
new file mode 100644
index 0000000..db02d00
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/environ.go
@@ -0,0 +1,38 @@
+// Copyright 2013 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+// EnvironTagKind is DEPRECATED: model tags are used instead.
+const EnvironTagKind = "environment"
+
+type EnvironTag struct {
+ uuid string
+}
+
+// NewEnvironTag returns the tag of an environment with the given environment UUID.
+func NewEnvironTag(uuid string) EnvironTag {
+ return EnvironTag{uuid: uuid}
+}
+
+// ParseEnvironTag parses an environ tag string.
+func ParseEnvironTag(environTag string) (EnvironTag, error) {
+ tag, err := ParseTag(environTag)
+ if err != nil {
+ return EnvironTag{}, err
+ }
+ et, ok := tag.(EnvironTag)
+ if !ok {
+ return EnvironTag{}, invalidTagError(environTag, EnvironTagKind)
+ }
+ return et, nil
+}
+
+func (t EnvironTag) String() string { return t.Kind() + "-" + t.Id() }
+func (t EnvironTag) Kind() string { return EnvironTagKind }
+func (t EnvironTag) Id() string { return t.uuid }
+
+// IsValidEnvironment returns whether id is a valid environment UUID.
+func IsValidEnvironment(id string) bool {
+ return validUUID.MatchString(id)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/filesystem.go b/automation/vendor/gopkg.in/juju/names.v2/filesystem.go
new file mode 100644
index 0000000..44ecc96
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/filesystem.go
@@ -0,0 +1,76 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+const FilesystemTagKind = "filesystem"
+
+// Filesystems may be bound to a machine, meaning that the filesystem cannot
+// exist without that machine. We encode this in the tag.
+var validFilesystem = regexp.MustCompile("^(" + MachineSnippet + "/)?" + NumberSnippet + "$")
+
+type FilesystemTag struct {
+ id string
+}
+
+func (t FilesystemTag) String() string { return t.Kind() + "-" + t.id }
+func (t FilesystemTag) Kind() string { return FilesystemTagKind }
+func (t FilesystemTag) Id() string { return filesystemTagSuffixToId(t.id) }
+
+// NewFilesystemTag returns the tag for the filesystem with the given name.
+// It will panic if the given filesystem name is not valid.
+func NewFilesystemTag(id string) FilesystemTag {
+ tag, ok := tagFromFilesystemId(id)
+ if !ok {
+ panic(fmt.Sprintf("%q is not a valid filesystem id", id))
+ }
+ return tag
+}
+
+// ParseFilesystemTag parses a filesystem tag string.
+func ParseFilesystemTag(filesystemTag string) (FilesystemTag, error) {
+ tag, err := ParseTag(filesystemTag)
+ if err != nil {
+ return FilesystemTag{}, err
+ }
+ fstag, ok := tag.(FilesystemTag)
+ if !ok {
+ return FilesystemTag{}, invalidTagError(filesystemTag, FilesystemTagKind)
+ }
+ return fstag, nil
+}
+
+// IsValidFilesystem returns whether id is a valid filesystem id.
+func IsValidFilesystem(id string) bool {
+ return validFilesystem.MatchString(id)
+}
+
+// FilesystemMachine returns the machine component of the filesystem
+// tag, and a boolean indicating whether or not there is a
+// machine component.
+func FilesystemMachine(tag FilesystemTag) (MachineTag, bool) {
+ id := tag.Id()
+ pos := strings.LastIndex(id, "/")
+ if pos == -1 {
+ return MachineTag{}, false
+ }
+ return NewMachineTag(id[:pos]), true
+}
+
+func tagFromFilesystemId(id string) (FilesystemTag, bool) {
+ if !IsValidFilesystem(id) {
+ return FilesystemTag{}, false
+ }
+ id = strings.Replace(id, "/", "-", -1)
+ return FilesystemTag{id}, true
+}
+
+func filesystemTagSuffixToId(s string) string {
+ return strings.Replace(s, "-", "/", -1)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/ipaddress.go b/automation/vendor/gopkg.in/juju/names.v2/ipaddress.go
new file mode 100644
index 0000000..b92a389
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/ipaddress.go
@@ -0,0 +1,46 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "github.com/juju/utils"
+)
+
+const IPAddressTagKind = "ipaddress"
+
+// IsValidIPAddress returns whether id is a valid IP address ID.
+// Here it simply is checked if it is a valid UUID.
+func IsValidIPAddress(id string) bool {
+ return utils.IsValidUUIDString(id)
+}
+
+type IPAddressTag struct {
+ id utils.UUID
+}
+
+func (t IPAddressTag) String() string { return t.Kind() + "-" + t.id.String() }
+func (t IPAddressTag) Kind() string { return IPAddressTagKind }
+func (t IPAddressTag) Id() string { return t.id.String() }
+
+// NewIPAddressTag returns the tag for the IP address with the given ID (UUID).
+func NewIPAddressTag(id string) IPAddressTag {
+ uuid, err := utils.UUIDFromString(id)
+ if err != nil {
+ panic(err)
+ }
+ return IPAddressTag{id: uuid}
+}
+
+// ParseIPAddressTag parses an IP address tag string.
+func ParseIPAddressTag(ipAddressTag string) (IPAddressTag, error) {
+ tag, err := ParseTag(ipAddressTag)
+ if err != nil {
+ return IPAddressTag{}, err
+ }
+ ipat, ok := tag.(IPAddressTag)
+ if !ok {
+ return IPAddressTag{}, invalidTagError(ipAddressTag, IPAddressTagKind)
+ }
+ return ipat, nil
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/machine.go b/automation/vendor/gopkg.in/juju/names.v2/machine.go
new file mode 100644
index 0000000..867aa72
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/machine.go
@@ -0,0 +1,60 @@
+// Copyright 2013 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "regexp"
+ "strings"
+)
+
+const MachineTagKind = "machine"
+
+const (
+ ContainerTypeSnippet = "[a-z]+"
+ ContainerSnippet = "/" + ContainerTypeSnippet + "/" + NumberSnippet + ""
+ MachineSnippet = NumberSnippet + "(?:" + ContainerSnippet + ")*"
+)
+
+var validMachine = regexp.MustCompile("^" + MachineSnippet + "$")
+
+// IsValidMachine returns whether id is a valid machine id.
+func IsValidMachine(id string) bool {
+ return validMachine.MatchString(id)
+}
+
+// IsContainerMachine returns whether id is a valid container machine id.
+func IsContainerMachine(id string) bool {
+ return validMachine.MatchString(id) && strings.Contains(id, "/")
+}
+
+type MachineTag struct {
+ id string
+}
+
+func (t MachineTag) String() string { return t.Kind() + "-" + t.id }
+func (t MachineTag) Kind() string { return MachineTagKind }
+func (t MachineTag) Id() string { return machineTagSuffixToId(t.id) }
+
+// NewMachineTag returns the tag for the machine with the given id.
+func NewMachineTag(id string) MachineTag {
+ id = strings.Replace(id, "/", "-", -1)
+ return MachineTag{id: id}
+}
+
+// ParseMachineTag parses a machine tag string.
+func ParseMachineTag(machineTag string) (MachineTag, error) {
+ tag, err := ParseTag(machineTag)
+ if err != nil {
+ return MachineTag{}, err
+ }
+ mt, ok := tag.(MachineTag)
+ if !ok {
+ return MachineTag{}, invalidTagError(machineTag, MachineTagKind)
+ }
+ return mt, nil
+}
+
+func machineTagSuffixToId(s string) string {
+ return strings.Replace(s, "-", "/", -1)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/model.go b/automation/vendor/gopkg.in/juju/names.v2/model.go
new file mode 100644
index 0000000..60e0db2
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/model.go
@@ -0,0 +1,52 @@
+// Copyright 2016 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "regexp"
+)
+
+const ModelTagKind = "model"
+
+// ModelTag represents a tag used to describe a model.
+type ModelTag struct {
+ uuid string
+}
+
+var validUUID = regexp.MustCompile(`[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`)
+
+// Lowercase letters, digits and (non-leading) hyphens, as per LP:1568944 #5.
+var validModelName = regexp.MustCompile(`^[a-z0-9]+[a-z0-9-]*$`)
+
+// NewModelTag returns the tag of an model with the given model UUID.
+func NewModelTag(uuid string) ModelTag {
+ return ModelTag{uuid: uuid}
+}
+
+// ParseModelTag parses an environ tag string.
+func ParseModelTag(modelTag string) (ModelTag, error) {
+ tag, err := ParseTag(modelTag)
+ if err != nil {
+ return ModelTag{}, err
+ }
+ et, ok := tag.(ModelTag)
+ if !ok {
+ return ModelTag{}, invalidTagError(modelTag, ModelTagKind)
+ }
+ return et, nil
+}
+
+func (t ModelTag) String() string { return t.Kind() + "-" + t.Id() }
+func (t ModelTag) Kind() string { return ModelTagKind }
+func (t ModelTag) Id() string { return t.uuid }
+
+// IsValidModel returns whether id is a valid model UUID.
+func IsValidModel(id string) bool {
+ return validUUID.MatchString(id)
+}
+
+// IsValidModelName returns whether name is a valid string safe for a model name.
+func IsValidModelName(name string) bool {
+ return validModelName.MatchString(name)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/payload.go b/automation/vendor/gopkg.in/juju/names.v2/payload.go
new file mode 100644
index 0000000..c911ffd
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/payload.go
@@ -0,0 +1,74 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "regexp"
+
+ "github.com/juju/utils"
+)
+
+const (
+ // PayloadTagKind is used as the prefix for the string
+ // representation of payload tags.
+ PayloadTagKind = "payload"
+
+ // This can be expanded later, as needed.
+ payloadClass = "([a-zA-Z](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)"
+)
+
+var validPayload = regexp.MustCompile("^" + payloadClass + "$")
+
+// IsValidPayload returns whether id is a valid Juju ID for
+// a charm payload. The ID must be a valid alpha-numeric (plus hyphens).
+func IsValidPayload(id string) bool {
+ return validPayload.MatchString(id)
+}
+
+// For compatibility with Juju 1.25, UUIDs are also supported.
+func isValidPayload(id string) bool {
+ return IsValidPayload(id) || utils.IsValidUUIDString(id)
+}
+
+// PayloadTag represents a charm payload.
+type PayloadTag struct {
+ id string
+}
+
+// NewPayloadTag returns the tag for a charm's payload with the given id.
+func NewPayloadTag(id string) PayloadTag {
+ return PayloadTag{
+ id: id,
+ }
+}
+
+// ParsePayloadTag parses a payload tag string.
+// So ParsePayloadTag(tag.String()) === tag.
+func ParsePayloadTag(tag string) (PayloadTag, error) {
+ t, err := ParseTag(tag)
+ if err != nil {
+ return PayloadTag{}, err
+ }
+ pt, ok := t.(PayloadTag)
+ if !ok {
+ return PayloadTag{}, invalidTagError(tag, PayloadTagKind)
+ }
+ return pt, nil
+}
+
+// Kind implements Tag.
+func (t PayloadTag) Kind() string {
+ return PayloadTagKind
+}
+
+// Id implements Tag.Id. It always returns the same ID with which
+// it was created. So NewPayloadTag(x).Id() == x for all valid x.
+func (t PayloadTag) Id() string {
+ return t.id
+}
+
+// String implements Tag.
+func (t PayloadTag) String() string {
+ return tagString(t)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/relation.go b/automation/vendor/gopkg.in/juju/names.v2/relation.go
new file mode 100644
index 0000000..b61696c
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/relation.go
@@ -0,0 +1,67 @@
+// Copyright 2013 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+const RelationTagKind = "relation"
+
+const RelationSnippet = "[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*"
+
+// Relation keys have the format "application1:relName1 application2:relName2".
+// Except the peer relations, which have the format "application:relName"
+// Relation tags have the format "relation-application1.rel1#application2.rel2".
+// For peer relations, the format is "relation-application.rel"
+
+var (
+ validRelation = regexp.MustCompile("^" + ApplicationSnippet + ":" + RelationSnippet + " " + ApplicationSnippet + ":" + RelationSnippet + "$")
+ validPeerRelation = regexp.MustCompile("^" + ApplicationSnippet + ":" + RelationSnippet + "$")
+)
+
+// IsValidRelation returns whether key is a valid relation key.
+func IsValidRelation(key string) bool {
+ return validRelation.MatchString(key) || validPeerRelation.MatchString(key)
+}
+
+type RelationTag struct {
+ key string
+}
+
+func (t RelationTag) String() string { return t.Kind() + "-" + t.key }
+func (t RelationTag) Kind() string { return RelationTagKind }
+func (t RelationTag) Id() string { return relationTagSuffixToKey(t.key) }
+
+// NewRelationTag returns the tag for the relation with the given key.
+func NewRelationTag(relationKey string) RelationTag {
+ if !IsValidRelation(relationKey) {
+ panic(fmt.Sprintf("%q is not a valid relation key", relationKey))
+ }
+ // Replace both ":" with "." and the " " with "#".
+ relationKey = strings.Replace(relationKey, ":", ".", 2)
+ relationKey = strings.Replace(relationKey, " ", "#", 1)
+ return RelationTag{key: relationKey}
+}
+
+// ParseRelationTag parses a relation tag string.
+func ParseRelationTag(relationTag string) (RelationTag, error) {
+ tag, err := ParseTag(relationTag)
+ if err != nil {
+ return RelationTag{}, err
+ }
+ rt, ok := tag.(RelationTag)
+ if !ok {
+ return RelationTag{}, invalidTagError(relationTag, RelationTagKind)
+ }
+ return rt, nil
+}
+
+func relationTagSuffixToKey(s string) string {
+ // Replace both "." with ":" and the "#" with " ".
+ s = strings.Replace(s, ".", ":", 2)
+ return strings.Replace(s, "#", " ", 1)
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/space.go b/automation/vendor/gopkg.in/juju/names.v2/space.go
new file mode 100644
index 0000000..049e1c7
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/space.go
@@ -0,0 +1,50 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+)
+
+const (
+ SpaceTagKind = "space"
+ SpaceSnippet = "(?:[a-z0-9]+(?:-[a-z0-9]+)*)"
+)
+
+var validSpace = regexp.MustCompile("^" + SpaceSnippet + "$")
+
+// IsValidSpace reports whether name is a valid space name.
+func IsValidSpace(name string) bool {
+ return validSpace.MatchString(name)
+}
+
+type SpaceTag struct {
+ name string
+}
+
+func (t SpaceTag) String() string { return t.Kind() + "-" + t.Id() }
+func (t SpaceTag) Kind() string { return SpaceTagKind }
+func (t SpaceTag) Id() string { return t.name }
+
+// NewSpaceTag returns the tag of a space with the given name.
+func NewSpaceTag(name string) SpaceTag {
+ if !IsValidSpace(name) {
+ panic(fmt.Sprintf("%q is not a valid space name", name))
+ }
+ return SpaceTag{name: name}
+}
+
+// ParseSpaceTag parses a space tag string.
+func ParseSpaceTag(spaceTag string) (SpaceTag, error) {
+ tag, err := ParseTag(spaceTag)
+ if err != nil {
+ return SpaceTag{}, err
+ }
+ nt, ok := tag.(SpaceTag)
+ if !ok {
+ return SpaceTag{}, invalidTagError(spaceTag, SpaceTagKind)
+ }
+ return nt, nil
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/storage.go b/automation/vendor/gopkg.in/juju/names.v2/storage.go
new file mode 100644
index 0000000..2314f87
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/storage.go
@@ -0,0 +1,86 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+const (
+ StorageTagKind = "storage"
+
+ // StorageNameSnippet is the regular expression that describes valid
+ // storage names (without the storage instance sequence number).
+ StorageNameSnippet = "(?:[a-z][a-z0-9]*(?:-[a-z0-9]*[a-z][a-z0-9]*)*)"
+)
+
+var validStorage = regexp.MustCompile("^(" + StorageNameSnippet + ")/" + NumberSnippet + "$")
+
+type StorageTag struct {
+ id string
+}
+
+func (t StorageTag) String() string { return t.Kind() + "-" + t.id }
+func (t StorageTag) Kind() string { return StorageTagKind }
+func (t StorageTag) Id() string { return storageTagSuffixToId(t.id) }
+
+// NewStorageTag returns the tag for the storage instance with the given ID.
+// It will panic if the given string is not a valid storage instance Id.
+func NewStorageTag(id string) StorageTag {
+ tag, ok := tagFromStorageId(id)
+ if !ok {
+ panic(fmt.Sprintf("%q is not a valid storage instance ID", id))
+ }
+ return tag
+}
+
+// ParseStorageTag parses a storage tag string.
+func ParseStorageTag(s string) (StorageTag, error) {
+ tag, err := ParseTag(s)
+ if err != nil {
+ return StorageTag{}, err
+ }
+ st, ok := tag.(StorageTag)
+ if !ok {
+ return StorageTag{}, invalidTagError(s, StorageTagKind)
+ }
+ return st, nil
+}
+
+// IsValidStorage returns whether id is a valid storage instance ID.
+func IsValidStorage(id string) bool {
+ return validStorage.MatchString(id)
+}
+
+// StorageName returns the storage name from a storage instance ID.
+// StorageName returns an error if "id" is not a valid storage
+// instance ID.
+func StorageName(id string) (string, error) {
+ s := validStorage.FindStringSubmatch(id)
+ if s == nil {
+ return "", fmt.Errorf("%q is not a valid storage instance ID", id)
+ }
+ return s[1], nil
+}
+
+func tagFromStorageId(id string) (StorageTag, bool) {
+ // replace only the last "/" with "-".
+ i := strings.LastIndex(id, "/")
+ if i <= 0 || !IsValidStorage(id) {
+ return StorageTag{}, false
+ }
+ id = id[:i] + "-" + id[i+1:]
+ return StorageTag{id}, true
+}
+
+func storageTagSuffixToId(s string) string {
+ // Replace only the last "-" with "/", as it is valid for storage
+ // names to contain hyphens.
+ if i := strings.LastIndex(s, "-"); i > 0 {
+ s = s[:i] + "/" + s[i+1:]
+ }
+ return s
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/subnet.go b/automation/vendor/gopkg.in/juju/names.v2/subnet.go
new file mode 100644
index 0000000..281eb0f
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/subnet.go
@@ -0,0 +1,49 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "net"
+)
+
+const SubnetTagKind = "subnet"
+
+// IsValidSubnet returns whether cidr is a valid subnet CIDR.
+func IsValidSubnet(cidr string) bool {
+ _, ipNet, err := net.ParseCIDR(cidr)
+ if err == nil && ipNet.String() == cidr {
+ return true
+ }
+ return false
+}
+
+type SubnetTag struct {
+ cidr string
+}
+
+func (t SubnetTag) String() string { return t.Kind() + "-" + t.cidr }
+func (t SubnetTag) Kind() string { return SubnetTagKind }
+func (t SubnetTag) Id() string { return t.cidr }
+
+// NewSubnetTag returns the tag for subnet with the given CIDR.
+func NewSubnetTag(cidr string) SubnetTag {
+ if !IsValidSubnet(cidr) {
+ panic(fmt.Sprintf("%s is not a valid subnet CIDR", cidr))
+ }
+ return SubnetTag{cidr: cidr}
+}
+
+// ParseSubnetTag parses a subnet tag string.
+func ParseSubnetTag(subnetTag string) (SubnetTag, error) {
+ tag, err := ParseTag(subnetTag)
+ if err != nil {
+ return SubnetTag{}, err
+ }
+ subt, ok := tag.(SubnetTag)
+ if !ok {
+ return SubnetTag{}, invalidTagError(subnetTag, SubnetTagKind)
+ }
+ return subt, nil
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/tag.go b/automation/vendor/gopkg.in/juju/names.v2/tag.go
new file mode 100644
index 0000000..9feae00
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/tag.go
@@ -0,0 +1,216 @@
+// Copyright 2015 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/juju/errors"
+ "github.com/juju/utils"
+)
+
+// A Tag tags things that are taggable. Its purpose is to uniquely
+// identify some resource and provide a consistent representation of
+// that identity in both a human-readable and a machine-friendly format.
+// The latter benefits use of the tag in over-the-wire transmission
+// (e.g. in HTTP RPC calls) and in filename paths. The human-readable
+// tag "name" is available through the Id method. The machine-friendly
+// representation is provided by the String method.
+//
+// The ParseTag function may be used to build a tag from the machine-
+// formatted string. As well each kind of tag has its own Parse* method.
+// Each kind also has a New* method (e.g. NewMachineTag) which produces
+// a tag from the human-readable tag "ID".
+//
+// In the context of juju, the API *must* use tags to represent the
+// various juju entities. This contrasts with user-facing code, where
+// tags *must not* be used. Internal to juju the use of tags is a
+// judgement call based on the situation.
+type Tag interface {
+ // Kind returns the kind of the tag.
+ // This method is for legacy compatibility, callers should
+ // use equality or type assertions to verify the Kind, or type
+ // of a Tag.
+ Kind() string
+
+ // Id returns an identifier for this Tag.
+ // The contents and format of the identifier are specific
+ // to the implementer of the Tag.
+ Id() string
+
+ fmt.Stringer // all Tags should be able to print themselves
+}
+
+// tagString returns the canonical string representation of a tag.
+// It round-trips with splitTag().
+func tagString(tag Tag) string {
+ return tag.Kind() + "-" + tag.Id()
+}
+
+// TagKind returns one of the *TagKind constants for the given tag, or
+// an error if none matches.
+func TagKind(tag string) (string, error) {
+ i := strings.Index(tag, "-")
+ if i <= 0 || !validKinds(tag[:i]) {
+ return "", fmt.Errorf("%q is not a valid tag", tag)
+ }
+ return tag[:i], nil
+}
+
+func validKinds(kind string) bool {
+ switch kind {
+ case UnitTagKind, MachineTagKind, ApplicationTagKind, EnvironTagKind, UserTagKind,
+ RelationTagKind, ActionTagKind, VolumeTagKind, CharmTagKind, StorageTagKind,
+ FilesystemTagKind, IPAddressTagKind, SpaceTagKind, SubnetTagKind,
+ PayloadTagKind, ModelTagKind, ControllerTagKind, CloudTagKind, CloudCredentialTagKind:
+ return true
+ }
+ return false
+}
+
+func splitTag(tag string) (string, string, error) {
+ kind, err := TagKind(tag)
+ if err != nil {
+ return "", "", err
+ }
+ return kind, tag[len(kind)+1:], nil
+}
+
+// ParseTag parses a string representation into a Tag.
+func ParseTag(tag string) (Tag, error) {
+ kind, id, err := splitTag(tag)
+ if err != nil {
+ return nil, invalidTagError(tag, "")
+ }
+ switch kind {
+ case UnitTagKind:
+ id = unitTagSuffixToId(id)
+ if !IsValidUnit(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewUnitTag(id), nil
+ case MachineTagKind:
+ id = machineTagSuffixToId(id)
+ if !IsValidMachine(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewMachineTag(id), nil
+ case ApplicationTagKind:
+ if !IsValidApplication(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewApplicationTag(id), nil
+ case UserTagKind:
+ if !IsValidUser(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewUserTag(id), nil
+ case EnvironTagKind:
+ if !IsValidEnvironment(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewEnvironTag(id), nil
+ case ModelTagKind:
+ if !IsValidModel(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewModelTag(id), nil
+ case ControllerTagKind:
+ if !IsValidController(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewControllerTag(id), nil
+
+ case RelationTagKind:
+ id = relationTagSuffixToKey(id)
+ if !IsValidRelation(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewRelationTag(id), nil
+ case ActionTagKind:
+ if !IsValidAction(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewActionTag(id), nil
+ case VolumeTagKind:
+ id = volumeTagSuffixToId(id)
+ if !IsValidVolume(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewVolumeTag(id), nil
+ case CharmTagKind:
+ if !IsValidCharm(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewCharmTag(id), nil
+ case StorageTagKind:
+ id = storageTagSuffixToId(id)
+ if !IsValidStorage(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewStorageTag(id), nil
+ case FilesystemTagKind:
+ id = filesystemTagSuffixToId(id)
+ if !IsValidFilesystem(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewFilesystemTag(id), nil
+ case IPAddressTagKind:
+ uuid, err := utils.UUIDFromString(id)
+ if err != nil {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewIPAddressTag(uuid.String()), nil
+ case SubnetTagKind:
+ if !IsValidSubnet(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewSubnetTag(id), nil
+ case SpaceTagKind:
+ if !IsValidSpace(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewSpaceTag(id), nil
+ case PayloadTagKind:
+ if !isValidPayload(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewPayloadTag(id), nil
+ case CloudTagKind:
+ if !IsValidCloud(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewCloudTag(id), nil
+ case CloudCredentialTagKind:
+ id, err = cloudCredentialTagSuffixToId(id)
+ if err != nil {
+ return nil, errors.Wrap(err, invalidTagError(tag, kind))
+ }
+ if !IsValidCloudCredential(id) {
+ return nil, invalidTagError(tag, kind)
+ }
+ return NewCloudCredentialTag(id), nil
+ default:
+ return nil, invalidTagError(tag, "")
+ }
+}
+
+func invalidTagError(tag, kind string) error {
+ if kind != "" {
+ return fmt.Errorf("%q is not a valid %s tag", tag, kind)
+ }
+ return fmt.Errorf("%q is not a valid tag", tag)
+}
+
+// ReadableString returns a human-readable string from the tag passed in.
+// It currently supports unit and machine tags. Support for additional types
+// can be added in as needed.
+func ReadableString(tag Tag) string {
+ if tag == nil {
+ return ""
+ }
+
+ return tag.Kind() + " " + tag.Id()
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/unit.go b/automation/vendor/gopkg.in/juju/names.v2/unit.go
new file mode 100644
index 0000000..3f3a026
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/unit.go
@@ -0,0 +1,79 @@
+// Copyright 2013 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+const UnitTagKind = "unit"
+
+var validUnit = regexp.MustCompile("^(" + ApplicationSnippet + ")/" + NumberSnippet + "$")
+
+type UnitTag struct {
+ name string
+}
+
+func (t UnitTag) String() string { return t.Kind() + "-" + t.name }
+func (t UnitTag) Kind() string { return UnitTagKind }
+func (t UnitTag) Id() string { return unitTagSuffixToId(t.name) }
+
+// NewUnitTag returns the tag for the unit with the given name.
+// It will panic if the given unit name is not valid.
+func NewUnitTag(unitName string) UnitTag {
+ tag, ok := tagFromUnitName(unitName)
+ if !ok {
+ panic(fmt.Sprintf("%q is not a valid unit name", unitName))
+ }
+ return tag
+}
+
+// ParseUnitTag parses a unit tag string.
+func ParseUnitTag(unitTag string) (UnitTag, error) {
+ tag, err := ParseTag(unitTag)
+ if err != nil {
+ return UnitTag{}, err
+ }
+ ut, ok := tag.(UnitTag)
+ if !ok {
+ return UnitTag{}, invalidTagError(unitTag, UnitTagKind)
+ }
+ return ut, nil
+}
+
+// IsValidUnit returns whether name is a valid unit name.
+func IsValidUnit(name string) bool {
+ return validUnit.MatchString(name)
+}
+
+// UnitApplication returns the name of the application that the unit is
+// associated with. It returns an error if unitName is not a valid unit name.
+func UnitApplication(unitName string) (string, error) {
+ s := validUnit.FindStringSubmatch(unitName)
+ if s == nil {
+ return "", fmt.Errorf("%q is not a valid unit name", unitName)
+ }
+ return s[1], nil
+}
+
+func tagFromUnitName(unitName string) (UnitTag, bool) {
+ // Replace only the last "/" with "-".
+ i := strings.LastIndex(unitName, "/")
+ if i <= 0 || !IsValidUnit(unitName) {
+ return UnitTag{}, false
+ }
+ unitName = unitName[:i] + "-" + unitName[i+1:]
+ return UnitTag{name: unitName}, true
+}
+
+func unitTagSuffixToId(s string) string {
+ // Replace only the last "-" with "/", as it is valid for application
+ // names to contain hyphens.
+ if i := strings.LastIndex(s, "-"); i > 0 {
+ s = s[:i] + "/" + s[i+1:]
+ }
+ return s
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/user.go b/automation/vendor/gopkg.in/juju/names.v2/user.go
new file mode 100644
index 0000000..332ee27
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/user.go
@@ -0,0 +1,130 @@
+// Copyright 2013 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+)
+
+const (
+ UserTagKind = "user"
+ LocalUserDomain = "local"
+)
+
+var (
+ // TODO this does not allow single character usernames or
+ // domains. Is that deliberate?
+ // https://github.com/juju/names/issues/54
+ validUserNameSnippet = "[a-zA-Z0-9][a-zA-Z0-9.+-]*[a-zA-Z0-9]"
+ validUserSnippet = fmt.Sprintf("(?:%s(?:@%s)?)", validUserNameSnippet, validUserNameSnippet)
+ validName = regexp.MustCompile(fmt.Sprintf("^(?P<name>%s)(?:@(?P<domain>%s))?$", validUserNameSnippet, validUserNameSnippet))
+ validUserName = regexp.MustCompile("^" + validUserNameSnippet + "$")
+)
+
+// IsValidUser returns whether id is a valid user id.
+// Valid users may or may not be qualified with an
+// @domain suffix. Examples of valid users include
+// bob, bob@local, bob@somewhere-else, 0-a-f@123.
+func IsValidUser(id string) bool {
+ return validName.MatchString(id)
+}
+
+// IsValidUserName returns whether the given
+// name is a valid name part of a user. That is,
+// usernames with a domain suffix will return
+// false.
+func IsValidUserName(name string) bool {
+ return validUserName.MatchString(name)
+}
+
+// IsValidUserDomain returns whether the given user
+// domain is valid.
+func IsValidUserDomain(domain string) bool {
+ return validUserName.MatchString(domain)
+}
+
+// UserTag represents a user that may be stored locally
+// or associated with some external domain.
+type UserTag struct {
+ name string
+ domain string
+}
+
+func (t UserTag) Kind() string { return UserTagKind }
+func (t UserTag) String() string { return UserTagKind + "-" + t.Id() }
+
+// Id implements Tag.Id. Local users will always have
+// an Id value without any domain.
+func (t UserTag) Id() string {
+ if t.domain == "" || t.domain == LocalUserDomain {
+ return t.name
+ }
+ return t.name + "@" + t.domain
+}
+
+// Name returns the name part of the user name
+// without its associated domain.
+func (t UserTag) Name() string { return t.name }
+
+// IsLocal returns true if the tag represents a local user.
+// Users without an explicit domain are considered local.
+func (t UserTag) IsLocal() bool {
+ return t.Domain() == LocalUserDomain || t.Domain() == ""
+}
+
+// Domain returns the user domain. Users in the local database
+// are from the LocalDomain. Other users are considered 'remote' users.
+func (t UserTag) Domain() string {
+ return t.domain
+}
+
+// WithDomain returns a copy of the user tag with the
+// domain changed to the given argument.
+// The domain must satisfy IsValidUserDomain
+// or this function will panic.
+func (t UserTag) WithDomain(domain string) UserTag {
+ if !IsValidUserDomain(domain) {
+ panic(fmt.Sprintf("invalid user domain %q", domain))
+ }
+ return UserTag{
+ name: t.name,
+ domain: domain,
+ }
+}
+
+// NewUserTag returns the tag for the user with the given name.
+// It panics if the user name does not satisfy IsValidUser.
+func NewUserTag(userName string) UserTag {
+ parts := validName.FindStringSubmatch(userName)
+ if len(parts) != 3 {
+ panic(fmt.Sprintf("invalid user tag %q", userName))
+ }
+ domain := parts[2]
+ if domain == LocalUserDomain {
+ domain = ""
+ }
+ return UserTag{name: parts[1], domain: domain}
+}
+
+// NewLocalUserTag returns the tag for a local user with the given name.
+func NewLocalUserTag(name string) UserTag {
+ if !IsValidUserName(name) {
+ panic(fmt.Sprintf("invalid user name %q", name))
+ }
+ return UserTag{name: name}
+}
+
+// ParseUserTag parses a user tag string.
+func ParseUserTag(tag string) (UserTag, error) {
+ t, err := ParseTag(tag)
+ if err != nil {
+ return UserTag{}, err
+ }
+ ut, ok := t.(UserTag)
+ if !ok {
+ return UserTag{}, invalidTagError(tag, UserTagKind)
+ }
+ return ut, nil
+}
diff --git a/automation/vendor/gopkg.in/juju/names.v2/volume.go b/automation/vendor/gopkg.in/juju/names.v2/volume.go
new file mode 100644
index 0000000..2c1fd1b
--- /dev/null
+++ b/automation/vendor/gopkg.in/juju/names.v2/volume.go
@@ -0,0 +1,76 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package names
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+const VolumeTagKind = "volume"
+
+// Volumes may be bound to a machine, meaning that the volume cannot
+// exist without that machine. We encode this in the tag to allow
+var validVolume = regexp.MustCompile("^(" + MachineSnippet + "/)?" + NumberSnippet + "$")
+
+type VolumeTag struct {
+ id string
+}
+
+func (t VolumeTag) String() string { return t.Kind() + "-" + t.id }
+func (t VolumeTag) Kind() string { return VolumeTagKind }
+func (t VolumeTag) Id() string { return volumeTagSuffixToId(t.id) }
+
+// NewVolumeTag returns the tag for the volume with the given ID.
+// It will panic if the given volume ID is not valid.
+func NewVolumeTag(id string) VolumeTag {
+ tag, ok := tagFromVolumeId(id)
+ if !ok {
+ panic(fmt.Sprintf("%q is not a valid volume ID", id))
+ }
+ return tag
+}
+
+// ParseVolumeTag parses a volume tag string.
+func ParseVolumeTag(volumeTag string) (VolumeTag, error) {
+ tag, err := ParseTag(volumeTag)
+ if err != nil {
+ return VolumeTag{}, err
+ }
+ dt, ok := tag.(VolumeTag)
+ if !ok {
+ return VolumeTag{}, invalidTagError(volumeTag, VolumeTagKind)
+ }
+ return dt, nil
+}
+
+// IsValidVolume returns whether id is a valid volume ID.
+func IsValidVolume(id string) bool {
+ return validVolume.MatchString(id)
+}
+
+// VolumeMachine returns the machine component of the volume
+// tag, and a boolean indicating whether or not there is a
+// machine component.
+func VolumeMachine(tag VolumeTag) (MachineTag, bool) {
+ id := tag.Id()
+ pos := strings.LastIndex(id, "/")
+ if pos == -1 {
+ return MachineTag{}, false
+ }
+ return NewMachineTag(id[:pos]), true
+}
+
+func tagFromVolumeId(id string) (VolumeTag, bool) {
+ if !IsValidVolume(id) {
+ return VolumeTag{}, false
+ }
+ id = strings.Replace(id, "/", "-", -1)
+ return VolumeTag{id}, true
+}
+
+func volumeTagSuffixToId(s string) string {
+ return strings.Replace(s, "-", "/", -1)
+}