builder/triton: Vendor dependencies
This commit is contained in:
parent
e15be036d7
commit
396147b1ee
|
@ -0,0 +1,26 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# IntelliJ files
|
||||||
|
.idea
|
||||||
|
*.iml
|
|
@ -0,0 +1,373 @@
|
||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,2 @@
|
||||||
|
gocommon
|
||||||
|
========
|
|
@ -0,0 +1,110 @@
|
||||||
|
//
|
||||||
|
// gocommon - Go library to interact with the JoyentCloud
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Joyent Inc.
|
||||||
|
//
|
||||||
|
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
//
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
joyenthttp "github.com/joyent/gocommon/http"
|
||||||
|
"github.com/joyent/gosign/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The HTTP request methods.
|
||||||
|
GET = "GET"
|
||||||
|
POST = "POST"
|
||||||
|
PUT = "PUT"
|
||||||
|
DELETE = "DELETE"
|
||||||
|
HEAD = "HEAD"
|
||||||
|
COPY = "COPY"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client implementations sends service requests to the JoyentCloud.
|
||||||
|
type Client interface {
|
||||||
|
SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error)
|
||||||
|
// MakeServiceURL prepares a full URL to a service endpoint, with optional
|
||||||
|
// URL parts. It uses the first endpoint it can find for the given service type.
|
||||||
|
MakeServiceURL(parts []string) string
|
||||||
|
SignURL(path string, expires time.Time) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This client sends requests without authenticating.
|
||||||
|
type client struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
logger *log.Logger
|
||||||
|
baseURL string
|
||||||
|
creds *auth.Credentials
|
||||||
|
httpClient *joyenthttp.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Client = (*client)(nil)
|
||||||
|
|
||||||
|
func newClient(baseURL string, credentials *auth.Credentials, httpClient *joyenthttp.Client, logger *log.Logger) Client {
|
||||||
|
client := client{baseURL: baseURL, logger: logger, creds: credentials, httpClient: httpClient}
|
||||||
|
return &client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(baseURL, apiVersion string, credentials *auth.Credentials, logger *log.Logger) Client {
|
||||||
|
sharedHttpClient := joyenthttp.New(credentials, apiVersion, logger)
|
||||||
|
return newClient(baseURL, credentials, sharedHttpClient, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) sendRequest(method, url, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) {
|
||||||
|
if request.ReqValue != nil || response.RespValue != nil {
|
||||||
|
err = c.httpClient.JsonRequest(method, url, rfc1123Date, request, response)
|
||||||
|
} else {
|
||||||
|
err = c.httpClient.BinaryRequest(method, url, rfc1123Date, request, response)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) {
|
||||||
|
url := c.MakeServiceURL([]string{c.creds.UserAuthentication.User, apiCall})
|
||||||
|
err = c.sendRequest(method, url, rfc1123Date, request, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeURL(base string, parts []string) string {
|
||||||
|
if !strings.HasSuffix(base, "/") && len(parts) > 0 {
|
||||||
|
base += "/"
|
||||||
|
}
|
||||||
|
if parts[1] == "" {
|
||||||
|
return base + parts[0]
|
||||||
|
}
|
||||||
|
return base + strings.Join(parts, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) MakeServiceURL(parts []string) string {
|
||||||
|
return makeURL(c.baseURL, parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SignURL(path string, expires time.Time) (string, error) {
|
||||||
|
parsedURL, err := url.Parse(c.baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("bad Manta endpoint URL %q: %v", c.baseURL, err)
|
||||||
|
}
|
||||||
|
userAuthentication := c.creds.UserAuthentication
|
||||||
|
userAuthentication.Algorithm = "RSA-SHA1"
|
||||||
|
keyId := url.QueryEscape(fmt.Sprintf("/%s/keys/%s", userAuthentication.User, c.creds.MantaKeyId))
|
||||||
|
params := fmt.Sprintf("algorithm=%s&expires=%d&keyId=%s", userAuthentication.Algorithm, expires.Unix(), keyId)
|
||||||
|
signingLine := fmt.Sprintf("GET\n%s\n%s\n%s", parsedURL.Host, path, params)
|
||||||
|
|
||||||
|
signature, err := auth.GetSignature(userAuthentication, signingLine)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("cannot generate URL signature: %v", err)
|
||||||
|
}
|
||||||
|
signedURL := fmt.Sprintf("%s%s?%s&signature=%s", c.baseURL, path, params, url.QueryEscape(signature))
|
||||||
|
return signedURL, nil
|
||||||
|
}
|
|
@ -0,0 +1,292 @@
|
||||||
|
//
|
||||||
|
// gocommon - Go library to interact with the JoyentCloud
|
||||||
|
// This package provides an Error implementation which knows about types of error, and which has support
|
||||||
|
// for error causes.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Joyent Inc.
|
||||||
|
//
|
||||||
|
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
//
|
||||||
|
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Code string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Public available error types.
|
||||||
|
// These errors are provided because they are specifically required by business logic in the callers.
|
||||||
|
BadRequestError = Code("BadRequest")
|
||||||
|
InternalErrorError = Code("InternalError")
|
||||||
|
InvalidArgumentError = Code("InvalidArgument")
|
||||||
|
InvalidCredentialsError = Code("InvalidCredentials")
|
||||||
|
InvalidHeaderError = Code("InvalidHeader")
|
||||||
|
InvalidVersionError = Code("InvalidVersion")
|
||||||
|
MissingParameterError = Code("MissinParameter")
|
||||||
|
NotAuthorizedError = Code("NotAuthorized")
|
||||||
|
RequestThrottledError = Code("RequestThrottled")
|
||||||
|
RequestTooLargeError = Code("RequestTooLarge")
|
||||||
|
RequestMovedError = Code("RequestMoved")
|
||||||
|
ResourceNotFoundError = Code("ResourceNotFound")
|
||||||
|
UnknownErrorError = Code("UnkownError")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error instances store an optional error cause.
|
||||||
|
type Error interface {
|
||||||
|
error
|
||||||
|
Cause() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type gojoyentError struct {
|
||||||
|
error
|
||||||
|
errcode Code
|
||||||
|
cause error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type checks.
|
||||||
|
var _ Error = (*gojoyentError)(nil)
|
||||||
|
|
||||||
|
// Code returns the error code.
|
||||||
|
func (err *gojoyentError) code() Code {
|
||||||
|
if err.errcode != UnknownErrorError {
|
||||||
|
return err.errcode
|
||||||
|
}
|
||||||
|
if e, ok := err.cause.(*gojoyentError); ok {
|
||||||
|
return e.code()
|
||||||
|
}
|
||||||
|
return UnknownErrorError
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause returns the error cause.
|
||||||
|
func (err *gojoyentError) Cause() error {
|
||||||
|
return err.cause
|
||||||
|
}
|
||||||
|
|
||||||
|
// CausedBy returns true if this error or its cause are of the specified error code.
|
||||||
|
func (err *gojoyentError) causedBy(code Code) bool {
|
||||||
|
if err.code() == code {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if cause, ok := err.cause.(*gojoyentError); ok {
|
||||||
|
return cause.code() == code
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error fulfills the error interface, taking account of any caused by error.
|
||||||
|
func (err *gojoyentError) Error() string {
|
||||||
|
if err.cause != nil {
|
||||||
|
return fmt.Sprintf("%v\ncaused by: %v", err.error, err.cause)
|
||||||
|
}
|
||||||
|
return err.error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsBadRequest(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(BadRequestError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInternalError(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(InternalErrorError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInvalidArgument(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(InvalidArgumentError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInvalidCredentials(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(InvalidCredentialsError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInvalidHeader(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(InvalidHeaderError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInvalidVersion(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(InvalidVersionError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsMissingParameter(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(MissingParameterError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNotAuthorized(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(NotAuthorizedError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRequestThrottled(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(RequestThrottledError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRequestTooLarge(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(RequestTooLargeError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRequestMoved(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(RequestMovedError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsResourceNotFound(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(ResourceNotFoundError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsUnknownError(err error) bool {
|
||||||
|
if e, ok := err.(*gojoyentError); ok {
|
||||||
|
return e.causedBy(UnknownErrorError)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Error instance with the specified cause.
|
||||||
|
func makeErrorf(code Code, cause error, format string, args ...interface{}) Error {
|
||||||
|
return &gojoyentError{
|
||||||
|
errcode: code,
|
||||||
|
error: fmt.Errorf(format, args...),
|
||||||
|
cause: cause,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new UnknownError Error instance with the specified cause.
|
||||||
|
func Newf(cause error, format string, args ...interface{}) Error {
|
||||||
|
return makeErrorf(UnknownErrorError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new BadRequest Error instance with the specified cause.
|
||||||
|
func NewBadRequestf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Bad Request: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(BadRequestError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new InternalError Error instance with the specified cause.
|
||||||
|
func NewInternalErrorf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Internal Error: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(InternalErrorError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new InvalidArgument Error instance with the specified cause.
|
||||||
|
func NewInvalidArgumentf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Invalid Argument: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(InvalidArgumentError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new InvalidCredentials Error instance with the specified cause.
|
||||||
|
func NewInvalidCredentialsf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Invalid Credentials: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(InvalidCredentialsError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new InvalidHeader Error instance with the specified cause.
|
||||||
|
func NewInvalidHeaderf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Invalid Header: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(InvalidHeaderError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new InvalidVersion Error instance with the specified cause.
|
||||||
|
func NewInvalidVersionf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Invalid Version: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(InvalidVersionError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new MissingParameter Error instance with the specified cause.
|
||||||
|
func NewMissingParameterf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Missing Parameter: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(MissingParameterError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new NotAuthorized Error instance with the specified cause.
|
||||||
|
func NewNotAuthorizedf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Not Authorized: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(NotAuthorizedError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new RequestThrottled Error instance with the specified cause.
|
||||||
|
func NewRequestThrottledf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Request Throttled: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(RequestThrottledError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new RequestTooLarge Error instance with the specified cause.
|
||||||
|
func NewRequestTooLargef(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Request Too Large: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(RequestTooLargeError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new RequestMoved Error instance with the specified cause.
|
||||||
|
func NewRequestMovedf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Request Moved: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(RequestMovedError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new ResourceNotFound Error instance with the specified cause.
|
||||||
|
func NewResourceNotFoundf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Resource Not Found: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(ResourceNotFoundError, cause, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new UnknownError Error instance with the specified cause.
|
||||||
|
func NewUnknownErrorf(cause error, context interface{}, format string, args ...interface{}) Error {
|
||||||
|
if format == "" {
|
||||||
|
format = fmt.Sprintf("Unknown Error: %s", context)
|
||||||
|
}
|
||||||
|
return makeErrorf(UnknownErrorError, cause, format, args...)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* The gocommon package collects common packages to interact with the Joyent Public Cloud and Joyent Manta services.
|
||||||
|
*
|
||||||
|
* The gocommon package is structured as follow:
|
||||||
|
*
|
||||||
|
* - gocommon/client. Client for sending requests.
|
||||||
|
* - gocommon/errors. Joyent specific errors.
|
||||||
|
* - gocommon/http. HTTP client for sending requests.
|
||||||
|
* - gocommon/jpc. This package provides common structures and functions across packages.
|
||||||
|
* - gocommon/testing. Testing Suite for local testing.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Joyent Inc.
|
||||||
|
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package gocommon
|
|
@ -0,0 +1,427 @@
|
||||||
|
//
|
||||||
|
// gocommon - Go library to interact with the JoyentCloud
|
||||||
|
// An HTTP Client which sends json and binary requests, handling data marshalling and response processing.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Joyent Inc.
|
||||||
|
//
|
||||||
|
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
//
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
"github.com/joyent/gocommon/jpc"
|
||||||
|
"github.com/joyent/gosign/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
contentTypeJSON = "application/json"
|
||||||
|
contentTypeOctetStream = "application/octet-stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
http.Client
|
||||||
|
maxSendAttempts int
|
||||||
|
credentials *auth.Credentials
|
||||||
|
apiVersion string
|
||||||
|
logger *log.Logger
|
||||||
|
trace bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorResponse) Error() string {
|
||||||
|
return fmt.Sprintf("Failed: %d: %s", e.Code, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorWrapper struct {
|
||||||
|
Error ErrorResponse `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestData struct {
|
||||||
|
ReqHeaders http.Header
|
||||||
|
Params *url.Values
|
||||||
|
ReqValue interface{}
|
||||||
|
ReqReader io.Reader
|
||||||
|
ReqLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseData struct {
|
||||||
|
ExpectedStatus []int
|
||||||
|
RespHeaders *http.Header
|
||||||
|
RespValue interface{}
|
||||||
|
RespReader io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The maximum number of times to try sending a request before we give up
|
||||||
|
// (assuming any unsuccessful attempts can be sensibly tried again).
|
||||||
|
MaxSendAttempts = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new http *Client using the default net/http client.
|
||||||
|
func New(credentials *auth.Credentials, apiVersion string, logger *log.Logger) *Client {
|
||||||
|
return &Client{*http.DefaultClient, MaxSendAttempts, credentials, apiVersion, logger, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTrace allows control over whether requests will write their
|
||||||
|
// contents to the logger supplied during construction. Note that this
|
||||||
|
// is not safe to call from multiple go-routines.
|
||||||
|
func (client *Client) SetTrace(traceEnabled bool) {
|
||||||
|
client.trace = traceEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func gojoyentAgent() string {
|
||||||
|
return fmt.Sprintf("gocommon (%s)", gocommon.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createHeaders(extraHeaders http.Header, credentials *auth.Credentials, contentType, rfc1123Date,
|
||||||
|
apiVersion string, isMantaRequest bool) (http.Header, error) {
|
||||||
|
|
||||||
|
headers := make(http.Header)
|
||||||
|
if extraHeaders != nil {
|
||||||
|
for header, values := range extraHeaders {
|
||||||
|
for _, value := range values {
|
||||||
|
headers.Add(header, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extraHeaders.Get("Content-Type") == "" {
|
||||||
|
headers.Add("Content-Type", contentType)
|
||||||
|
}
|
||||||
|
if extraHeaders.Get("Accept") == "" {
|
||||||
|
headers.Add("Accept", contentType)
|
||||||
|
}
|
||||||
|
if rfc1123Date != "" {
|
||||||
|
headers.Set("Date", rfc1123Date)
|
||||||
|
} else {
|
||||||
|
headers.Set("Date", getDateForRegion(credentials, isMantaRequest))
|
||||||
|
}
|
||||||
|
authHeaders, err := auth.CreateAuthorizationHeader(headers, credentials, isMantaRequest)
|
||||||
|
if err != nil {
|
||||||
|
return http.Header{}, err
|
||||||
|
}
|
||||||
|
headers.Set("Authorization", authHeaders)
|
||||||
|
if apiVersion != "" {
|
||||||
|
headers.Set("X-Api-Version", apiVersion)
|
||||||
|
}
|
||||||
|
headers.Add("User-Agent", gojoyentAgent())
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDateForRegion(credentials *auth.Credentials, isManta bool) string {
|
||||||
|
if isManta {
|
||||||
|
location, _ := time.LoadLocation(jpc.Locations["us-east-1"])
|
||||||
|
return time.Now().In(location).Format(time.RFC1123)
|
||||||
|
} else {
|
||||||
|
location, _ := time.LoadLocation(jpc.Locations[credentials.Region()])
|
||||||
|
return time.Now().In(location).Format(time.RFC1123)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsonRequest JSON encodes and sends the object in reqData.ReqValue (if any) to the specified URL.
|
||||||
|
// Optional method arguments are passed using the RequestData object.
|
||||||
|
// Relevant RequestData fields:
|
||||||
|
// ReqHeaders: additional HTTP header values to add to the request.
|
||||||
|
// ExpectedStatus: the allowed HTTP response status values, else an error is returned.
|
||||||
|
// ReqValue: the data object to send.
|
||||||
|
// RespValue: the data object to decode the result into.
|
||||||
|
func (c *Client) JsonRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) {
|
||||||
|
err = nil
|
||||||
|
var body []byte
|
||||||
|
if request.Params != nil {
|
||||||
|
url += "?" + request.Params.Encode()
|
||||||
|
}
|
||||||
|
if request.ReqValue != nil {
|
||||||
|
body, err = json.Marshal(request.ReqValue)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Newf(err, "failed marshalling the request body")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeJSON, rfc1123Date, c.apiVersion,
|
||||||
|
isMantaRequest(url, c.credentials.UserAuthentication.User))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
respBody, respHeader, err := c.sendRequest(
|
||||||
|
method, url, bytes.NewReader(body), len(body), headers, response.ExpectedStatus, c.logger)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer respBody.Close()
|
||||||
|
respData, err := ioutil.ReadAll(respBody)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Newf(err, "failed reading the response body")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(respData) > 0 {
|
||||||
|
if response.RespValue != nil {
|
||||||
|
if dest, ok := response.RespValue.(*[]byte); ok {
|
||||||
|
*dest = respData
|
||||||
|
//err = decodeJSON(bytes.NewReader(respData), false, response.RespValue)
|
||||||
|
//if err != nil {
|
||||||
|
// err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData)
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
err = json.Unmarshal(respData, response.RespValue)
|
||||||
|
if err != nil {
|
||||||
|
err = decodeJSON(bytes.NewReader(respData), true, response.RespValue)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if respHeader != nil {
|
||||||
|
response.RespHeaders = respHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJSON(r io.Reader, multiple bool, into interface{}) error {
|
||||||
|
d := json.NewDecoder(r)
|
||||||
|
if multiple {
|
||||||
|
return decodeStream(d, into)
|
||||||
|
}
|
||||||
|
return d.Decode(into)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeStream(d *json.Decoder, into interface{}) error {
|
||||||
|
t := reflect.TypeOf(into)
|
||||||
|
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
|
||||||
|
return fmt.Errorf("unexpected type %s", t)
|
||||||
|
}
|
||||||
|
elemType := t.Elem().Elem()
|
||||||
|
slice := reflect.ValueOf(into).Elem()
|
||||||
|
for {
|
||||||
|
val := reflect.New(elemType)
|
||||||
|
if err := d.Decode(val.Interface()); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slice.Set(reflect.Append(slice, val.Elem()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends the byte array in reqData.ReqValue (if any) to the specified URL.
|
||||||
|
// Optional method arguments are passed using the RequestData object.
|
||||||
|
// Relevant RequestData fields:
|
||||||
|
// ReqHeaders: additional HTTP header values to add to the request.
|
||||||
|
// ExpectedStatus: the allowed HTTP response status values, else an error is returned.
|
||||||
|
// ReqReader: an io.Reader providing the bytes to send.
|
||||||
|
// RespReader: assigned an io.ReadCloser instance used to read the returned data..
|
||||||
|
func (c *Client) BinaryRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) {
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
if request.Params != nil {
|
||||||
|
url += "?" + request.Params.Encode()
|
||||||
|
}
|
||||||
|
headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeOctetStream, rfc1123Date,
|
||||||
|
c.apiVersion, isMantaRequest(url, c.credentials.UserAuthentication.User))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
respBody, respHeader, err := c.sendRequest(
|
||||||
|
method, url, request.ReqReader, request.ReqLength, headers, response.ExpectedStatus, c.logger)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if response.RespReader != nil {
|
||||||
|
response.RespReader = respBody
|
||||||
|
}
|
||||||
|
if respHeader != nil {
|
||||||
|
response.RespHeaders = respHeader
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends the specified request to URL and checks that the HTTP response status is as expected.
|
||||||
|
// reqReader: a reader returning the data to send.
|
||||||
|
// length: the number of bytes to send.
|
||||||
|
// headers: HTTP headers to include with the request.
|
||||||
|
// expectedStatus: a slice of allowed response status codes.
|
||||||
|
func (c *Client) sendRequest(method, URL string, reqReader io.Reader, length int, headers http.Header,
|
||||||
|
expectedStatus []int, logger *log.Logger) (rc io.ReadCloser, respHeader *http.Header, err error) {
|
||||||
|
reqData := make([]byte, length)
|
||||||
|
if reqReader != nil {
|
||||||
|
nrRead, err := io.ReadFull(reqReader, reqData)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Newf(err, "failed reading the request data, read %v of %v bytes", nrRead, length)
|
||||||
|
return rc, respHeader, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawResp, err := c.sendRateLimitedRequest(method, URL, headers, reqData, logger)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger != nil && c.trace {
|
||||||
|
logger.Printf("Request: %s %s\n", method, URL)
|
||||||
|
logger.Printf("Request header: %s\n", headers)
|
||||||
|
logger.Printf("Request body: %s\n", reqData)
|
||||||
|
logger.Printf("Response: %s\n", rawResp.Status)
|
||||||
|
logger.Printf("Response header: %s\n", rawResp.Header)
|
||||||
|
logger.Printf("Response body: %s\n", rawResp.Body)
|
||||||
|
logger.Printf("Response error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
foundStatus := false
|
||||||
|
if len(expectedStatus) == 0 {
|
||||||
|
expectedStatus = []int{http.StatusOK}
|
||||||
|
}
|
||||||
|
for _, status := range expectedStatus {
|
||||||
|
if rawResp.StatusCode == status {
|
||||||
|
foundStatus = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundStatus && len(expectedStatus) > 0 {
|
||||||
|
err = handleError(URL, rawResp)
|
||||||
|
rawResp.Body.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return rawResp.Body, &rawResp.Header, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte,
|
||||||
|
logger *log.Logger) (resp *http.Response, err error) {
|
||||||
|
for i := 0; i < c.maxSendAttempts; i++ {
|
||||||
|
var reqReader io.Reader
|
||||||
|
if reqData != nil {
|
||||||
|
reqReader = bytes.NewReader(reqData)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(method, URL, reqReader)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Newf(err, "failed creating the request %s", URL)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Setting req.Close to true to avoid malformed HTTP version "nullHTTP/1.1" error
|
||||||
|
// See http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
|
||||||
|
req.Close = true
|
||||||
|
for header, values := range headers {
|
||||||
|
for _, value := range values {
|
||||||
|
req.Header.Add(header, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.ContentLength = int64(len(reqData))
|
||||||
|
resp, err = c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed executing the request %s", URL)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusRequestEntityTooLarge || resp.Header.Get("Retry-After") == "" {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
retryAfter, err := strconv.ParseFloat(resp.Header.Get("Retry-After"), 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Newf(err, "Invalid Retry-After header %s", URL)
|
||||||
|
}
|
||||||
|
if retryAfter == 0 {
|
||||||
|
return nil, errors.Newf(err, "Resource limit exeeded at URL %s", URL)
|
||||||
|
}
|
||||||
|
if logger != nil {
|
||||||
|
logger.Println("Too many requests, retrying in %dms.", int(retryAfter*1000))
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(retryAfter) * time.Second)
|
||||||
|
}
|
||||||
|
return nil, errors.Newf(err, "Maximum number of attempts (%d) reached sending request to %s", c.maxSendAttempts, URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HttpError struct {
|
||||||
|
StatusCode int
|
||||||
|
Data map[string][]string
|
||||||
|
Url string
|
||||||
|
ResponseMessage string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HttpError) Error() string {
|
||||||
|
return fmt.Sprintf("request %q returned unexpected status %d with body %q",
|
||||||
|
e.Url,
|
||||||
|
e.StatusCode,
|
||||||
|
e.ResponseMessage,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HTTP response status code was not one of those expected, so we construct an error.
|
||||||
|
// NotFound (404) codes have their own NotFound error type.
|
||||||
|
// We also make a guess at duplicate value errors.
|
||||||
|
func handleError(URL string, resp *http.Response) error {
|
||||||
|
errBytes, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
errInfo := string(errBytes)
|
||||||
|
// Check if we have a JSON representation of the failure, if so decode it.
|
||||||
|
if resp.Header.Get("Content-Type") == contentTypeJSON {
|
||||||
|
var errResponse ErrorResponse
|
||||||
|
if err := json.Unmarshal(errBytes, &errResponse); err == nil {
|
||||||
|
errInfo = errResponse.Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httpError := &HttpError{
|
||||||
|
resp.StatusCode, map[string][]string(resp.Header), URL, errInfo,
|
||||||
|
}
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusBadRequest:
|
||||||
|
return errors.NewBadRequestf(httpError, "", "Bad request %s", URL)
|
||||||
|
case http.StatusUnauthorized:
|
||||||
|
return errors.NewNotAuthorizedf(httpError, "", "Unauthorised URL %s", URL)
|
||||||
|
//return errors.NewInvalidCredentialsf(httpError, "", "Unauthorised URL %s", URL)
|
||||||
|
case http.StatusForbidden:
|
||||||
|
//return errors.
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return errors.NewResourceNotFoundf(httpError, "", "Resource not found %s", URL)
|
||||||
|
case http.StatusMethodNotAllowed:
|
||||||
|
//return errors.
|
||||||
|
case http.StatusNotAcceptable:
|
||||||
|
return errors.NewInvalidHeaderf(httpError, "", "Invalid Header %s", URL)
|
||||||
|
case http.StatusConflict:
|
||||||
|
return errors.NewMissingParameterf(httpError, "", "Missing parameters %s", URL)
|
||||||
|
//return errors.NewInvalidArgumentf(httpError, "", "Invalid parameter %s", URL)
|
||||||
|
case http.StatusRequestEntityTooLarge:
|
||||||
|
return errors.NewRequestTooLargef(httpError, "", "Request too large %s", URL)
|
||||||
|
case http.StatusUnsupportedMediaType:
|
||||||
|
//return errors.
|
||||||
|
case http.StatusServiceUnavailable:
|
||||||
|
return errors.NewInternalErrorf(httpError, "", "Internal error %s", URL)
|
||||||
|
case 420:
|
||||||
|
// SlowDown
|
||||||
|
return errors.NewRequestThrottledf(httpError, "", "Request throttled %s", URL)
|
||||||
|
case 422:
|
||||||
|
// Unprocessable Entity
|
||||||
|
return errors.NewInvalidArgumentf(httpError, "", "Invalid parameters %s", URL)
|
||||||
|
case 449:
|
||||||
|
// RetryWith
|
||||||
|
return errors.NewInvalidVersionf(httpError, "", "Invalid version %s", URL)
|
||||||
|
//RequestMovedError -> ?
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.NewUnknownErrorf(httpError, "", "Unknown error %s", URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMantaRequest(url, user string) bool {
|
||||||
|
return strings.Contains(url, "/"+user+"/stor") || strings.Contains(url, "/"+user+"/jobs") || strings.Contains(url, "/"+user+"/public")
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* gocommon - Go library to interact with the JoyentCloud
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Joyent Inc.
|
||||||
|
*
|
||||||
|
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/joyent/gosign/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Environment variables
|
||||||
|
SdcAccount = "SDC_ACCOUNT"
|
||||||
|
SdcKeyId = "SDC_KEY_ID"
|
||||||
|
SdcUrl = "SDC_URL"
|
||||||
|
MantaUser = "MANTA_USER"
|
||||||
|
MantaKeyId = "MANTA_KEY_ID"
|
||||||
|
MantaUrl = "MANTA_URL"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Locations = map[string]string{
|
||||||
|
"us-east-1": "America/New_York",
|
||||||
|
"us-west-1": "America/Los_Angeles",
|
||||||
|
"us-sw-1": "America/Los_Angeles",
|
||||||
|
"eu-ams-1": "Europe/Amsterdam",
|
||||||
|
}
|
||||||
|
|
||||||
|
// getConfig returns the value of the first available environment
|
||||||
|
// variable, among the given ones.
|
||||||
|
func getConfig(envVars ...string) (value string) {
|
||||||
|
value = ""
|
||||||
|
for _, v := range envVars {
|
||||||
|
value = os.Getenv(v)
|
||||||
|
if value != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUserHome returns the value of HOME environment
|
||||||
|
// variable for the user environment.
|
||||||
|
func getUserHome() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return os.Getenv("APPDATA")
|
||||||
|
} else {
|
||||||
|
return os.Getenv("HOME")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// credentialsFromEnv creates and initializes the credentials from the
|
||||||
|
// environment variables.
|
||||||
|
func credentialsFromEnv(key string) (*auth.Credentials, error) {
|
||||||
|
var keyName string
|
||||||
|
if key == "" {
|
||||||
|
keyName = getUserHome() + "/.ssh/id_rsa"
|
||||||
|
} else {
|
||||||
|
keyName = key
|
||||||
|
}
|
||||||
|
privateKey, err := ioutil.ReadFile(keyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
authentication, err := auth.NewAuth(getConfig(SdcAccount, MantaUser), string(privateKey), "rsa-sha256")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &auth.Credentials{
|
||||||
|
UserAuthentication: authentication,
|
||||||
|
SdcKeyId: getConfig(SdcKeyId),
|
||||||
|
SdcEndpoint: auth.Endpoint{URL: getConfig(SdcUrl)},
|
||||||
|
MantaKeyId: getConfig(MantaKeyId),
|
||||||
|
MantaEndpoint: auth.Endpoint{URL: getConfig(MantaUrl)},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompleteCredentialsFromEnv gets and verifies all the required
|
||||||
|
// authentication parameters have values in the environment.
|
||||||
|
func CompleteCredentialsFromEnv(keyName string) (cred *auth.Credentials, err error) {
|
||||||
|
cred, err = credentialsFromEnv(keyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := reflect.ValueOf(cred).Elem()
|
||||||
|
t := v.Type()
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
f := v.Field(i)
|
||||||
|
if f.String() == "" {
|
||||||
|
return nil, fmt.Errorf("Required environment variable not set for credentials attribute: %s", t.Field(i).Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cred, nil
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* gocommon - Go library to interact with the JoyentCloud
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Joyent Inc.
|
||||||
|
*
|
||||||
|
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package gocommon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VersionNum struct {
|
||||||
|
Major int
|
||||||
|
Minor int
|
||||||
|
Micro int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VersionNum) String() string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Micro)
|
||||||
|
}
|
||||||
|
|
||||||
|
var VersionNumber = VersionNum{
|
||||||
|
Major: 0,
|
||||||
|
Minor: 1,
|
||||||
|
Micro: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Version = VersionNumber.String()
|
|
@ -0,0 +1,373 @@
|
||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
Package cloudapi interacts with the Cloud API (http://apidocs.joyent.com/cloudapi/).
|
||||||
|
|
||||||
|
Licensed under the Mozilla Public License version 2.0
|
||||||
|
|
||||||
|
Copyright (c) Joyent Inc.
|
||||||
|
*/
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
jh "github.com/joyent/gocommon/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultAPIVersion defines the default version of the Cloud API to use
|
||||||
|
DefaultAPIVersion = "~7.3"
|
||||||
|
|
||||||
|
// CloudAPI URL parts
|
||||||
|
apiKeys = "keys"
|
||||||
|
apiPackages = "packages"
|
||||||
|
apiImages = "images"
|
||||||
|
apiDatacenters = "datacenters"
|
||||||
|
apiMachines = "machines"
|
||||||
|
apiMetadata = "metadata"
|
||||||
|
apiSnapshots = "snapshots"
|
||||||
|
apiTags = "tags"
|
||||||
|
apiAnalytics = "analytics"
|
||||||
|
apiInstrumentations = "instrumentations"
|
||||||
|
apiInstrumentationsValue = "value"
|
||||||
|
apiInstrumentationsRaw = "raw"
|
||||||
|
apiInstrumentationsHeatmap = "heatmap"
|
||||||
|
apiInstrumentationsImage = "image"
|
||||||
|
apiInstrumentationsDetails = "details"
|
||||||
|
apiUsage = "usage"
|
||||||
|
apiAudit = "audit"
|
||||||
|
apiFirewallRules = "fwrules"
|
||||||
|
apiFirewallRulesEnable = "enable"
|
||||||
|
apiFirewallRulesDisable = "disable"
|
||||||
|
apiNetworks = "networks"
|
||||||
|
apiFabricVLANs = "fabrics/default/vlans"
|
||||||
|
apiFabricNetworks = "networks"
|
||||||
|
apiNICs = "nics"
|
||||||
|
apiServices = "services"
|
||||||
|
|
||||||
|
// CloudAPI actions
|
||||||
|
actionExport = "export"
|
||||||
|
actionStop = "stop"
|
||||||
|
actionStart = "start"
|
||||||
|
actionReboot = "reboot"
|
||||||
|
actionResize = "resize"
|
||||||
|
actionRename = "rename"
|
||||||
|
actionEnableFw = "enable_firewall"
|
||||||
|
actionDisableFw = "disable_firewall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client provides a means to access the Joyent CloudAPI
|
||||||
|
type Client struct {
|
||||||
|
client client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Client.
|
||||||
|
func New(client client.Client) *Client {
|
||||||
|
return &Client{client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter represents a filter that can be applied to an API request.
|
||||||
|
type Filter struct {
|
||||||
|
v url.Values
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilter creates a new Filter.
|
||||||
|
func NewFilter() *Filter {
|
||||||
|
return &Filter{make(url.Values)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a value for the specified filter.
|
||||||
|
func (f *Filter) Set(filter, value string) {
|
||||||
|
f.v.Set(filter, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a value for the specified filter.
|
||||||
|
func (f *Filter) Add(filter, value string) {
|
||||||
|
f.v.Add(filter, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// request represents an API request
|
||||||
|
type request struct {
|
||||||
|
method string
|
||||||
|
url string
|
||||||
|
filter *Filter
|
||||||
|
reqValue interface{}
|
||||||
|
reqHeader http.Header
|
||||||
|
resp interface{}
|
||||||
|
respHeader *http.Header
|
||||||
|
expectedStatus int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to send an API request
|
||||||
|
func (c *Client) sendRequest(req request) (*jh.ResponseData, error) {
|
||||||
|
request := jh.RequestData{
|
||||||
|
ReqValue: req.reqValue,
|
||||||
|
ReqHeaders: req.reqHeader,
|
||||||
|
}
|
||||||
|
if req.filter != nil {
|
||||||
|
request.Params = &req.filter.v
|
||||||
|
}
|
||||||
|
if req.expectedStatus == 0 {
|
||||||
|
req.expectedStatus = http.StatusOK
|
||||||
|
}
|
||||||
|
respData := jh.ResponseData{
|
||||||
|
RespValue: req.resp,
|
||||||
|
RespHeaders: req.respHeader,
|
||||||
|
ExpectedStatus: []int{req.expectedStatus},
|
||||||
|
}
|
||||||
|
err := c.client.SendRequest(req.method, req.url, "", &request, &respData)
|
||||||
|
return &respData, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to create the API URL
|
||||||
|
func makeURL(parts ...string) string {
|
||||||
|
return path.Join(parts...)
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListDatacenters provides a list of all datacenters this cloud is aware of.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListDatacenters
|
||||||
|
func (c *Client) ListDatacenters() (map[string]interface{}, error) {
|
||||||
|
var resp map[string]interface{}
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiDatacenters,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of datcenters")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDatacenter gets an individual datacenter by name. Returns an HTTP redirect
|
||||||
|
// to your client, the datacenter URL is in the Location header.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetDatacenter
|
||||||
|
func (c *Client) GetDatacenter(datacenterName string) (string, error) {
|
||||||
|
var respHeader http.Header
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiDatacenters, datacenterName),
|
||||||
|
respHeader: &respHeader,
|
||||||
|
expectedStatus: http.StatusFound,
|
||||||
|
}
|
||||||
|
respData, err := c.sendRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Newf(err, "failed to get datacenter with name: %s", datacenterName)
|
||||||
|
}
|
||||||
|
return respData.RespHeaders.Get("Location"), nil
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FabricVLAN struct {
|
||||||
|
Id int16 `json:"vlan_id"` // Number between 0-4095 indicating VLAN Id
|
||||||
|
Name string `json:"name"` // Unique name to identify VLAN
|
||||||
|
Description string `json:"description,omitempty"` // Optional description of the VLAN
|
||||||
|
}
|
||||||
|
|
||||||
|
type FabricNetwork struct {
|
||||||
|
Id string `json:"id"` // Unique identifier for network
|
||||||
|
Name string `json:"name"` // Network name
|
||||||
|
Public bool `json:"public"` // Whether or not this is an RFC1918 network
|
||||||
|
Fabric bool `json:"fabric"` // Whether this network is on a fabric
|
||||||
|
Description string `json:"description"` // Optional description of network
|
||||||
|
Subnet string `json:"subnet"` // CIDR formatted string describing network
|
||||||
|
ProvisionStartIp string `json:"provision_start_ip"` // First IP on the network that can be assigned
|
||||||
|
ProvisionEndIp string `json:"provision_end_ip"` // Last assignable IP on the network
|
||||||
|
Gateway string `json:"gateway"` // Optional Gateway IP
|
||||||
|
Resolvers []string `json:"resolvers,omitempty"` // Array of IP addresses for resolvers
|
||||||
|
Routes map[string]string `json:"routes,omitempty"` // Map of CIDR block to Gateway IP Address
|
||||||
|
InternetNAT bool `json:"internet_nat"` // If a NAT zone is provisioned at Gateway IP Address
|
||||||
|
VLANId int16 `json:"vlan_id"` // VLAN network is on
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateFabricNetworkOpts struct {
|
||||||
|
Name string `json:"name"` // Network name
|
||||||
|
Description string `json:"description,omitempty"` // Optional description of network
|
||||||
|
Subnet string `json:"subnet"` // CIDR formatted string describing network
|
||||||
|
ProvisionStartIp string `json:"provision_start_ip"` // First IP on the network that can be assigned
|
||||||
|
ProvisionEndIp string `json:"provision_end_ip"` // Last assignable IP on the network
|
||||||
|
Gateway string `json:"gateway,omitempty"` // Optional Gateway IP
|
||||||
|
Resolvers []string `json:"resolvers,omitempty"` // Array of IP addresses for resolvers
|
||||||
|
Routes map[string]string `json:"routes,omitempty"` // Map of CIDR block to Gateway IP Address
|
||||||
|
InternetNAT bool `json:"internet_nat"` // If a NAT zone is provisioned at Gateway IP Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFabricVLANs lists VLANs
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#ListFabricVLANs
|
||||||
|
func (c *Client) ListFabricVLANs() ([]FabricVLAN, error) {
|
||||||
|
var resp []FabricVLAN
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiFabricVLANs,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of fabric VLANs")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFabricLAN retrieves a single VLAN by ID
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#GetFabricVLAN
|
||||||
|
func (c *Client) GetFabricVLAN(vlanID int16) (*FabricVLAN, error) {
|
||||||
|
var resp FabricVLAN
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID))),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get fabric VLAN with id %d", vlanID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFabricVLAN creates a new VLAN with the specified options
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#CreateFabricVLAN
|
||||||
|
func (c *Client) CreateFabricVLAN(vlan FabricVLAN) (*FabricVLAN, error) {
|
||||||
|
var resp FabricVLAN
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: apiFabricVLANs,
|
||||||
|
reqValue: vlan,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create fabric VLAN: %d - %s", vlan.Id, vlan.Name)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFabricVLAN updates a given VLAN with new fields
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#UpdateFabricVLAN
|
||||||
|
func (c *Client) UpdateFabricVLAN(vlan FabricVLAN) (*FabricVLAN, error) {
|
||||||
|
var resp FabricVLAN
|
||||||
|
req := request{
|
||||||
|
method: client.PUT,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlan.Id))),
|
||||||
|
reqValue: vlan,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to update fabric VLAN with id %d to %s - %s", vlan.Id, vlan.Name, vlan.Description)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFabricVLAN delets a given VLAN as specified by ID
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#DeleteFabricVLAN
|
||||||
|
func (c *Client) DeleteFabricVLAN(vlanID int16) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID))),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete fabric VLAN with id %d", vlanID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFabricNetworks lists the networks inside the given VLAN
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#ListFabricNetworks
|
||||||
|
func (c *Client) ListFabricNetworks(vlanID int16) ([]FabricNetwork, error) {
|
||||||
|
var resp []FabricNetwork
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of networks on fabric %d", vlanID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFabricNetwork gets a single network by VLAN and Network IDs
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#GetFabricNetwork
|
||||||
|
func (c *Client) GetFabricNetwork(vlanID int16, networkID string) (*FabricNetwork, error) {
|
||||||
|
var resp FabricNetwork
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks, networkID),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get fabric network %s on vlan %d", networkID, vlanID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFabricNetwork creates a new fabric network
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#CreateFabricNetwork
|
||||||
|
func (c *Client) CreateFabricNetwork(vlanID int16, opts CreateFabricNetworkOpts) (*FabricNetwork, error) {
|
||||||
|
var resp FabricNetwork
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks),
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create fabric network %s on vlan %d", opts.Name, vlanID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFabricNetwork deletes an existing fabric network
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#DeleteFabricNetwork
|
||||||
|
func (c *Client) DeleteFabricNetwork(vlanID int16, networkID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks, networkID),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete fabric network %s on vlan %d", networkID, vlanID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FirewallRule represent a firewall rule that can be specifed for a machine.
|
||||||
|
type FirewallRule struct {
|
||||||
|
Id string // Unique identifier for the rule
|
||||||
|
Enabled bool // Whether the rule is enabled or not
|
||||||
|
Rule string // Firewall rule in the form 'FROM <target a> TO <target b> <action> <protocol> <port>'
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFwRuleOpts represent the option that can be specified
|
||||||
|
// when creating a new firewall rule.
|
||||||
|
type CreateFwRuleOpts struct {
|
||||||
|
Enabled bool `json:"enabled"` // Whether to enable the rule or not
|
||||||
|
Rule string `json:"rule"` // Firewall rule in the form 'FROM <target a> TO <target b> <action> <protocol> <port>'
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFirewallRules lists all the firewall rules on record for a specified account.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListFirewallRules
|
||||||
|
func (c *Client) ListFirewallRules() ([]FirewallRule, error) {
|
||||||
|
var resp []FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiFirewallRules,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of firewall rules")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFirewallRule returns the specified firewall rule.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetFirewallRule
|
||||||
|
func (c *Client) GetFirewallRule(fwRuleID string) (*FirewallRule, error) {
|
||||||
|
var resp FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiFirewallRules, fwRuleID),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get firewall rule with id %s", fwRuleID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFirewallRule creates the firewall rule with the specified options.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateFirewallRule
|
||||||
|
func (c *Client) CreateFirewallRule(opts CreateFwRuleOpts) (*FirewallRule, error) {
|
||||||
|
var resp FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: apiFirewallRules,
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create firewall rule: %s", opts.Rule)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFirewallRule updates the specified firewall rule.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#UpdateFirewallRule
|
||||||
|
func (c *Client) UpdateFirewallRule(fwRuleID string, opts CreateFwRuleOpts) (*FirewallRule, error) {
|
||||||
|
var resp FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiFirewallRules, fwRuleID),
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to update firewall rule with id %s to %s", fwRuleID, opts.Rule)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableFirewallRule enables the given firewall rule record if it is disabled.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#EnableFirewallRule
|
||||||
|
func (c *Client) EnableFirewallRule(fwRuleID string) (*FirewallRule, error) {
|
||||||
|
var resp FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiFirewallRules, fwRuleID, apiFirewallRulesEnable),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to enable firewall rule with id %s", fwRuleID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableFirewallRule disables the given firewall rule record if it is enabled.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DisableFirewallRule
|
||||||
|
func (c *Client) DisableFirewallRule(fwRuleID string) (*FirewallRule, error) {
|
||||||
|
var resp FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiFirewallRules, fwRuleID, apiFirewallRulesDisable),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to disable firewall rule with id %s", fwRuleID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFirewallRule removes the given firewall rule record from all the required account machines.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteFirewallRule
|
||||||
|
func (c *Client) DeleteFirewallRule(fwRuleID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiFirewallRules, fwRuleID),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete firewall rule with id %s", fwRuleID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFirewallRuleMachines return the list of machines affected by the given firewall rule.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListFirewallRuleMachines
|
||||||
|
func (c *Client) ListFirewallRuleMachines(fwRuleID string) ([]Machine, error) {
|
||||||
|
var resp []Machine
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiFirewallRules, fwRuleID, apiMachines),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of machines affected by firewall rule wit id %s", fwRuleID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Image represent the software packages that will be available on newly provisioned machines
|
||||||
|
type Image struct {
|
||||||
|
Id string // Unique identifier for the image
|
||||||
|
Name string // Image friendly name
|
||||||
|
OS string // Underlying operating system
|
||||||
|
Version string // Image version
|
||||||
|
Type string // Image type, one of 'smartmachine' or 'virtualmachine'
|
||||||
|
Description string // Image description
|
||||||
|
Requirements map[string]interface{} // Minimum requirements for provisioning a machine with this image, e.g. 'password' indicates that a password must be provided
|
||||||
|
Homepage string // URL for a web page including detailed information for this image (new in API version 7.0)
|
||||||
|
PublishedAt string `json:"published_at"` // Time this image has been made publicly available (new in API version 7.0)
|
||||||
|
Public bool // Indicates if the image is publicly available (new in API version 7.1)
|
||||||
|
State string // Current image state. One of 'active', 'unactivated', 'disabled', 'creating', 'failed' (new in API version 7.1)
|
||||||
|
Tags map[string]string // A map of key/value pairs that allows clients to categorize images by any given criteria (new in API version 7.1)
|
||||||
|
EULA string // URL of the End User License Agreement (EULA) for the image (new in API version 7.1)
|
||||||
|
ACL []string // An array of account UUIDs given access to a private image. The field is only relevant to private images (new in API version 7.1)
|
||||||
|
Owner string // The UUID of the user owning the image
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportImageOpts represent the option that can be specified
|
||||||
|
// when exporting an image.
|
||||||
|
type ExportImageOpts struct {
|
||||||
|
MantaPath string `json:"manta_path"` // The Manta path prefix to use when exporting the image
|
||||||
|
}
|
||||||
|
|
||||||
|
// MantaLocation represent the properties that allow a user
|
||||||
|
// to retrieve the image file and manifest from Manta
|
||||||
|
type MantaLocation struct {
|
||||||
|
MantaURL string `json:"manta_url"` // Manta datacenter URL
|
||||||
|
ImagePath string `json:"image_path"` // Path to the image
|
||||||
|
ManifestPath string `json:"manifest_path"` // Path to the image manifest
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImageFromMachineOpts represent the option that can be specified
|
||||||
|
// when creating a new image from an existing machine.
|
||||||
|
type CreateImageFromMachineOpts struct {
|
||||||
|
Machine string `json:"machine"` // The machine UUID from which the image is to be created
|
||||||
|
Name string `json:"name"` // Image name
|
||||||
|
Version string `json:"version"` // Image version
|
||||||
|
Description string `json:"description,omitempty"` // Image description
|
||||||
|
Homepage string `json:"homepage,omitempty"` // URL for a web page including detailed information for this image
|
||||||
|
EULA string `json:"eula,omitempty"` // URL of the End User License Agreement (EULA) for the image
|
||||||
|
ACL []string `json:"acl,omitempty"` // An array of account UUIDs given access to a private image. The field is only relevant to private images
|
||||||
|
Tags map[string]string `json:"tags,omitempty"` // A map of key/value pairs that allows clients to categorize images by any given criteria
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListImages provides a list of images available in the datacenter.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListImages
|
||||||
|
func (c *Client) ListImages(filter *Filter) ([]Image, error) {
|
||||||
|
var resp []Image
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiImages,
|
||||||
|
filter: filter,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of images")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImage returns the image specified by imageId.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetImage
|
||||||
|
func (c *Client) GetImage(imageID string) (*Image, error) {
|
||||||
|
var resp Image
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiImages, imageID),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get image with id: %s", imageID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteImage (Beta) Delete the image specified by imageId. Must be image owner to do so.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteImage
|
||||||
|
func (c *Client) DeleteImage(imageID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiImages, imageID),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete image with id: %s", imageID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportImage (Beta) Exports an image to the specified Manta path.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListImages
|
||||||
|
func (c *Client) ExportImage(imageID string, opts ExportImageOpts) (*MantaLocation, error) {
|
||||||
|
var resp MantaLocation
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s", apiImages, imageID, actionExport),
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to export image %s to %s", imageID, opts.MantaPath)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImageFromMachine (Beta) Create a new custom image from a machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListImages
|
||||||
|
func (c *Client) CreateImageFromMachine(opts CreateImageFromMachineOpts) (*Image, error) {
|
||||||
|
var resp Image
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: apiImages,
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create image from machine %s", opts.Machine)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analytics represents the available analytics
|
||||||
|
type Analytics struct {
|
||||||
|
Modules map[string]interface{} // Namespace to organize metrics
|
||||||
|
Fields map[string]interface{} // Fields represent metadata by which data points can be filtered or decomposed
|
||||||
|
Types map[string]interface{} // Types are used with both metrics and fields for two purposes: to hint to clients at how to best label values, and to distinguish between numeric and discrete quantities.
|
||||||
|
Metrics map[string]interface{} // Metrics describe quantities which can be measured by the system
|
||||||
|
Transformations map[string]interface{} // Transformations are post-processing functions that can be applied to data when it's retrieved.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instrumentation specify which metric to collect, how frequently to aggregate data (e.g., every second, every hour, etc.)
|
||||||
|
// how much data to keep (e.g., 10 minutes' worth, 6 months' worth, etc.) and other configuration options
|
||||||
|
type Instrumentation struct {
|
||||||
|
Module string `json:"module"`
|
||||||
|
Stat string `json:"stat"`
|
||||||
|
Predicate string `json:"predicate"`
|
||||||
|
Decomposition []string `json:"decomposition"`
|
||||||
|
ValueDimension int `json:"value-dimenstion"`
|
||||||
|
ValueArity string `json:"value-arity"`
|
||||||
|
RetentionTime int `json:"retention-time"`
|
||||||
|
Granularity int `json:"granularitiy"`
|
||||||
|
IdleMax int `json:"idle-max"`
|
||||||
|
Transformations []string `json:"transformations"`
|
||||||
|
PersistData bool `json:"persist-data"`
|
||||||
|
Crtime int `json:"crtime"`
|
||||||
|
ValueScope string `json:"value-scope"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
Uris []Uri `json:"uris"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uri represents a Universal Resource Identifier
|
||||||
|
type Uri struct {
|
||||||
|
Uri string // Resource identifier
|
||||||
|
Name string // URI name
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationValue represents the data associated to an instrumentation for a point in time
|
||||||
|
type InstrumentationValue struct {
|
||||||
|
Value interface{}
|
||||||
|
Transformations map[string]interface{}
|
||||||
|
StartTime int
|
||||||
|
Duration int
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeatmapOpts represent the option that can be specified
|
||||||
|
// when retrieving an instrumentation.'s heatmap
|
||||||
|
type HeatmapOpts struct {
|
||||||
|
Height int `json:"height"` // Height of the image in pixels
|
||||||
|
Width int `json:"width"` // Width of the image in pixels
|
||||||
|
Ymin int `json:"ymin"` // Y-Axis value for the bottom of the image (default: 0)
|
||||||
|
Ymax int `json:"ymax"` // Y-Axis value for the top of the image (default: auto)
|
||||||
|
Nbuckets int `json:"nbuckets"` // Number of buckets in the vertical dimension
|
||||||
|
Selected []string `json:"selected"` // Array of field values to highlight, isolate or exclude
|
||||||
|
Isolate bool `json:"isolate"` // If true, only draw selected values
|
||||||
|
Exclude bool `json:"exclude"` // If true, don't draw selected values at all
|
||||||
|
Hues []string `json:"hues"` // Array of colors for highlighting selected field values
|
||||||
|
DecomposeAll bool `json:"decompose_all"` // Highlight all field values
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heatmap represents an instrumentation's heatmap
|
||||||
|
type Heatmap struct {
|
||||||
|
BucketTime int `json:"bucket_time"` // Time corresponding to the bucket (Unix seconds)
|
||||||
|
BucketYmin int `json:"bucket_ymin"` // Minimum y-axis value for the bucket
|
||||||
|
BucketYmax int `json:"bucket_ymax"` // Maximum y-axis value for the bucket
|
||||||
|
Present map[string]interface{} `json:"present"` // If the instrumentation defines a discrete decomposition, this property's value is an object whose keys are values of that field and whose values are the number of data points in that bucket for that key
|
||||||
|
Total int `json:"total"` // The total number of data points in the bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInstrumentationOpts represent the option that can be specified
|
||||||
|
// when creating a new instrumentation.
|
||||||
|
type CreateInstrumentationOpts struct {
|
||||||
|
Clone int `json:"clone"` // An existing instrumentation ID to be cloned
|
||||||
|
Module string `json:"module"` // Analytics module
|
||||||
|
Stat string `json:"stat"` // Analytics stat
|
||||||
|
Predicate string `json:"predicate"` // Instrumentation predicate, must be JSON string
|
||||||
|
Decomposition string `json:"decomposition"`
|
||||||
|
Granularity int `json:"granularity"` // Number of seconds between data points (default is 1)
|
||||||
|
RetentionTime int `json:"retention-time"` // How long to keep this instrumentation data for
|
||||||
|
PersistData bool `json:"persist-data"` // Whether or not to store this for historical analysis
|
||||||
|
IdleMax int `json:"idle-max"` // Number of seconds after which if the instrumentation or its data has not been accessed via the API the service may delete the instrumentation and its data
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescribeAnalytics retrieves the "schema" for instrumentations that can be created.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DescribeAnalytics
|
||||||
|
func (c *Client) DescribeAnalytics() (*Analytics, error) {
|
||||||
|
var resp Analytics
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiAnalytics,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get analytics")
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListInstrumentations retrieves all currently created instrumentations.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListInstrumentations
|
||||||
|
func (c *Client) ListInstrumentations() ([]Instrumentation, error) {
|
||||||
|
var resp []Instrumentation
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get instrumentations")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstrumentation retrieves the configuration for the specified instrumentation.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentation
|
||||||
|
func (c *Client) GetInstrumentation(instrumentationID string) (*Instrumentation, error) {
|
||||||
|
var resp Instrumentation
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get instrumentation with id %s", instrumentationID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstrumentationValue retrieves the data associated to an instrumentation
|
||||||
|
// for a point in time.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationValue
|
||||||
|
func (c *Client) GetInstrumentationValue(instrumentationID string) (*InstrumentationValue, error) {
|
||||||
|
var resp InstrumentationValue
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID, apiInstrumentationsValue, apiInstrumentationsRaw),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get value for instrumentation with id %s", instrumentationID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstrumentationHeatmap retrieves the specified instrumentation's heatmap.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationHeatmap
|
||||||
|
func (c *Client) GetInstrumentationHeatmap(instrumentationID string) (*Heatmap, error) {
|
||||||
|
var resp Heatmap
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID, apiInstrumentationsValue, apiInstrumentationsHeatmap, apiInstrumentationsImage),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get heatmap image for instrumentation with id %s", instrumentationID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstrumentationHeatmapDetails allows you to retrieve the bucket details
|
||||||
|
// for a heatmap.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationHeatmapDetails
|
||||||
|
func (c *Client) GetInstrumentationHeatmapDetails(instrumentationID string) (*Heatmap, error) {
|
||||||
|
var resp Heatmap
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID, apiInstrumentationsValue, apiInstrumentationsHeatmap, apiInstrumentationsDetails),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get heatmap details for instrumentation with id %s", instrumentationID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInstrumentation Creates an instrumentation. You can clone an existing
|
||||||
|
// instrumentation by passing in the parameter clone, which should be a numeric id
|
||||||
|
// of an existing instrumentation.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateInstrumentation
|
||||||
|
func (c *Client) CreateInstrumentation(opts CreateInstrumentationOpts) (*Instrumentation, error) {
|
||||||
|
var resp Instrumentation
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations),
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create instrumentation")
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteInstrumentation destroys an instrumentation.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteInstrumentation
|
||||||
|
func (c *Client) DeleteInstrumentation(instrumentationID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete instrumentation with id %s", instrumentationID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key represent a public key
|
||||||
|
type Key struct {
|
||||||
|
Name string // Name for the key
|
||||||
|
Fingerprint string // Key Fingerprint
|
||||||
|
Key string // OpenSSH formatted public key
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func (k Key) Equals(other Key) bool {
|
||||||
|
if k.Name == other.Name && k.Fingerprint == other.Fingerprint && k.Key == other.Key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// CreateKeyOpts represent the option that can be specified
|
||||||
|
// when creating a new key.
|
||||||
|
type CreateKeyOpts struct {
|
||||||
|
Name string `json:"name"` // Name for the key, optional
|
||||||
|
Key string `json:"key"` // OpenSSH formatted public key
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListKeys returns a list of public keys registered with a specific account.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListKeys
|
||||||
|
func (c *Client) ListKeys() ([]Key, error) {
|
||||||
|
var resp []Key
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiKeys,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of keys")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey returns the key identified by keyName.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetKey
|
||||||
|
func (c *Client) GetKey(keyName string) (*Key, error) {
|
||||||
|
var resp Key
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiKeys, keyName),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get key with name: %s", keyName)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateKey creates a new key with the specified options.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateKey
|
||||||
|
func (c *Client) CreateKey(opts CreateKeyOpts) (*Key, error) {
|
||||||
|
var resp Key
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: apiKeys,
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create key with name: %s", opts.Name)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKey deletes the key identified by keyName.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteKey
|
||||||
|
func (c *Client) DeleteKey(keyName string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiKeys, keyName),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete key with name: %s", keyName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListMachineFirewallRules lists all the firewall rules for the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineFirewallRules
|
||||||
|
func (c *Client) ListMachineFirewallRules(machineID string) ([]FirewallRule, error) {
|
||||||
|
var resp []FirewallRule
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiFirewallRules),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of firewall rules for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableFirewallMachine enables the firewall for the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#EnableMachineFirewall
|
||||||
|
func (c *Client) EnableFirewallMachine(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionEnableFw),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to enable firewall on machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableFirewallMachine disables the firewall for the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DisableMachineFirewall
|
||||||
|
func (c *Client) DisableFirewallMachine(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionDisableFw),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to disable firewall on machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateMachineMetadata updates the metadata for a given machine.
|
||||||
|
// Any metadata keys passed in here are created if they do not exist, and
|
||||||
|
// overwritten if they do.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#UpdateMachineMetadata
|
||||||
|
func (c *Client) UpdateMachineMetadata(machineID string, metadata map[string]string) (map[string]interface{}, error) {
|
||||||
|
var resp map[string]interface{}
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiMachines, machineID, apiMetadata),
|
||||||
|
reqValue: metadata,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to update metadata for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMachineMetadata returns the complete set of metadata associated with the
|
||||||
|
// specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineMetadata
|
||||||
|
func (c *Client) GetMachineMetadata(machineID string) (map[string]interface{}, error) {
|
||||||
|
var resp map[string]interface{}
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiMetadata),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of metadata for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMachineMetadata deletes a single metadata key from the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineMetadata
|
||||||
|
func (c *Client) DeleteMachineMetadata(machineID, metadataKey string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID, apiMetadata, metadataKey),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete metadata with key %s for machine with id %s", metadataKey, machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAllMachineMetadata deletes all metadata keys from the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteAllMachineMetadata
|
||||||
|
func (c *Client) DeleteAllMachineMetadata(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID, apiMetadata),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete metadata for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NICState represents the state of a NIC
|
||||||
|
type NICState string
|
||||||
|
|
||||||
|
var (
|
||||||
|
NICStateProvisioning NICState = "provisioning"
|
||||||
|
NICStateRunning NICState = "running"
|
||||||
|
NICStateStopped NICState = "stopped"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NIC represents a NIC on a machine
|
||||||
|
type NIC struct {
|
||||||
|
IP string `json:"ip"` // NIC's IPv4 Address
|
||||||
|
MAC string `json:"mac"` // NIC's MAC address
|
||||||
|
Primary bool `json:"primary"` // Whether this is the machine's primary NIC
|
||||||
|
Netmask string `json:"netmask"` // IPv4 netmask
|
||||||
|
Gateway string `json:"gateway"` // IPv4 gateway
|
||||||
|
State NICState `json:"state"` // Describes the state of the NIC (e.g. provisioning, running, or stopped)
|
||||||
|
Network string `json:"network"` // Network ID this NIC is attached to
|
||||||
|
}
|
||||||
|
|
||||||
|
type addNICOptions struct {
|
||||||
|
Network string `json:"network"` // UUID of network this NIC should attach to
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNICs lists all the NICs on a machine belonging to a given account
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#ListNics
|
||||||
|
func (c *Client) ListNICs(machineID string) ([]NIC, error) {
|
||||||
|
var resp []NIC
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiNICs),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to list NICs")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNIC gets a specific NIC on a machine belonging to a given account
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#GetNic
|
||||||
|
func (c *Client) GetNIC(machineID, MAC string) (*NIC, error) {
|
||||||
|
resp := new(NIC)
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiNICs, MAC),
|
||||||
|
resp: resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get NIC with MAC: %s", MAC)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNIC creates a new NIC on a machine belonging to a given account.
|
||||||
|
// *WARNING*: this causes the machine to reboot while adding the NIC.
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#AddNic
|
||||||
|
func (c *Client) AddNIC(machineID, networkID string) (*NIC, error) {
|
||||||
|
resp := new(NIC)
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiMachines, machineID, apiNICs),
|
||||||
|
reqValue: addNICOptions{networkID},
|
||||||
|
resp: resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to add NIC to machine %s on network: %s", machineID, networkID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNIC removes a NIC on a machine belonging to a given account.
|
||||||
|
// *WARNING*: this causes the machine to reboot while removing the NIC.
|
||||||
|
// See API docs: https://apidocs.joyent.com/cloudapi/#RemoveNic
|
||||||
|
func (c *Client) RemoveNIC(machineID, MAC string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID, apiNICs, MAC),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to remove NIC: %s", MAC)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Snapshot represent a point in time state of a machine.
|
||||||
|
type Snapshot struct {
|
||||||
|
Name string // Snapshot name
|
||||||
|
State string // Snapshot state
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotOpts represent the option that can be specified
|
||||||
|
// when creating a new machine snapshot.
|
||||||
|
type SnapshotOpts struct {
|
||||||
|
Name string `json:"name"` // Snapshot name
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMachineSnapshot creates a new snapshot for the machine with the options specified.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachineSnapshot
|
||||||
|
func (c *Client) CreateMachineSnapshot(machineID string, opts SnapshotOpts) (*Snapshot, error) {
|
||||||
|
var resp Snapshot
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiMachines, machineID, apiSnapshots),
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create snapshot %s from machine with id %s", opts.Name, machineID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartMachineFromSnapshot starts the machine from the specified snapshot.
|
||||||
|
// Machine must be in 'stopped' state.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#StartMachineFromSnapshot
|
||||||
|
func (c *Client) StartMachineFromSnapshot(machineID, snapshotName string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to start machine with id %s from snapshot %s", machineID, snapshotName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMachineSnapshots lists all snapshots for the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineSnapshots
|
||||||
|
func (c *Client) ListMachineSnapshots(machineID string) ([]Snapshot, error) {
|
||||||
|
var resp []Snapshot
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiSnapshots),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of snapshots for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMachineSnapshot returns the state of the specified snapshot.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineSnapshot
|
||||||
|
func (c *Client) GetMachineSnapshot(machineID, snapshotName string) (*Snapshot, error) {
|
||||||
|
var resp Snapshot
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get snapshot %s for machine with id %s", snapshotName, machineID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMachineSnapshot deletes the specified snapshot.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineSnapshot
|
||||||
|
func (c *Client) DeleteMachineSnapshot(machineID, snapshotName string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete snapshot %s for machine with id %s", snapshotName, machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddMachineTags adds additional tags to the specified machine.
|
||||||
|
// This API lets you append new tags, not overwrite existing tags.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#AddMachineTags
|
||||||
|
func (c *Client) AddMachineTags(machineID string, tags map[string]string) (map[string]string, error) {
|
||||||
|
var resp map[string]string
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: makeURL(apiMachines, machineID, apiTags),
|
||||||
|
reqValue: tags,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to add tags for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceMachineTags replaces existing tags for the specified machine.
|
||||||
|
// This API lets you overwrite existing tags, not append to existing tags.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ReplaceMachineTags
|
||||||
|
func (c *Client) ReplaceMachineTags(machineID string, tags map[string]string) (map[string]string, error) {
|
||||||
|
var resp map[string]string
|
||||||
|
req := request{
|
||||||
|
method: client.PUT,
|
||||||
|
url: makeURL(apiMachines, machineID, apiTags),
|
||||||
|
reqValue: tags,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to replace tags for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMachineTags returns the complete set of tags associated with the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineTags
|
||||||
|
func (c *Client) ListMachineTags(machineID string) (map[string]string, error) {
|
||||||
|
var resp map[string]string
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiTags),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of tags for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMachineTag returns the value for a single tag on the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineTag
|
||||||
|
func (c *Client) GetMachineTag(machineID, tagKey string) (string, error) {
|
||||||
|
var resp []byte
|
||||||
|
requestHeaders := make(http.Header)
|
||||||
|
requestHeaders.Set("Accept", "text/plain")
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiTags, tagKey),
|
||||||
|
resp: &resp,
|
||||||
|
reqHeader: requestHeaders,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return "", errors.Newf(err, "failed to get tag %s for machine with id %s", tagKey, machineID)
|
||||||
|
}
|
||||||
|
return string(resp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMachineTag deletes a single tag from the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTag
|
||||||
|
func (c *Client) DeleteMachineTag(machineID, tagKey string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID, apiTags, tagKey),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete tag with key %s for machine with id %s", tagKey, machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMachineTags deletes all tags from the specified machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTags
|
||||||
|
func (c *Client) DeleteMachineTags(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID, apiTags),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete tags for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,306 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Machine represent a provisioned virtual machines
|
||||||
|
type Machine struct {
|
||||||
|
Id string // Unique identifier for the image
|
||||||
|
Name string // Machine friendly name
|
||||||
|
Type string // Machine type, one of 'smartmachine' or 'virtualmachine'
|
||||||
|
State string // Current state of the machine
|
||||||
|
Dataset string // The dataset URN the machine was provisioned with. For new images/datasets this value will be the dataset id, i.e, same value than the image attribute
|
||||||
|
Memory int // The amount of memory the machine has (in Mb)
|
||||||
|
Disk int // The amount of disk the machine has (in Gb)
|
||||||
|
IPs []string // The IP addresses the machine has
|
||||||
|
Metadata map[string]string // Map of the machine metadata, e.g. authorized-keys
|
||||||
|
Tags map[string]string // Map of the machine tags
|
||||||
|
Created string // When the machine was created
|
||||||
|
Updated string // When the machine was updated
|
||||||
|
Package string // The name of the package used to create the machine
|
||||||
|
Image string // The image id the machine was provisioned with
|
||||||
|
PrimaryIP string // The primary (public) IP address for the machine
|
||||||
|
Networks []string // The network IDs for the machine
|
||||||
|
FirewallEnabled bool `json:"firewall_enabled"` // whether or not the firewall is enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two machines. Ignores state and timestamps.
|
||||||
|
func (m Machine) Equals(other Machine) bool {
|
||||||
|
if m.Id == other.Id && m.Name == other.Name && m.Type == other.Type && m.Dataset == other.Dataset &&
|
||||||
|
m.Memory == other.Memory && m.Disk == other.Disk && m.Package == other.Package && m.Image == other.Image &&
|
||||||
|
m.compareIPs(other) && m.compareMetadata(other) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to compare two machines IPs
|
||||||
|
func (m Machine) compareIPs(other Machine) bool {
|
||||||
|
if len(m.IPs) != len(other.IPs) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v := range m.IPs {
|
||||||
|
if v != other.IPs[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to compare two machines metadata
|
||||||
|
func (m Machine) compareMetadata(other Machine) bool {
|
||||||
|
if len(m.Metadata) != len(other.Metadata) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for k, v := range m.Metadata {
|
||||||
|
if v != other.Metadata[k] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMachineOpts represent the option that can be specified
|
||||||
|
// when creating a new machine.
|
||||||
|
type CreateMachineOpts struct {
|
||||||
|
Name string `json:"name"` // Machine friendly name, default is a randomly generated name
|
||||||
|
Package string `json:"package"` // Name of the package to use on provisioning
|
||||||
|
Image string `json:"image"` // The image UUID
|
||||||
|
Networks []string `json:"networks"` // Desired networks IDs
|
||||||
|
Metadata map[string]string `json:"-"` // An arbitrary set of metadata key/value pairs can be set at provision time
|
||||||
|
Tags map[string]string `json:"-"` // An arbitrary set of tags can be set at provision time
|
||||||
|
FirewallEnabled bool `json:"firewall_enabled"` // Completely enable or disable firewall for this machine (new in API version 7.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuditAction represents an action/event accomplished by a machine.
|
||||||
|
type AuditAction struct {
|
||||||
|
Action string // Action name
|
||||||
|
Parameters map[string]interface{} // Original set of parameters sent when the action was requested
|
||||||
|
Time string // When the action finished
|
||||||
|
Success string // Either 'yes' or 'no', depending on the action successfulness
|
||||||
|
Caller Caller // Account requesting the action
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caller represents an account requesting an action.
|
||||||
|
type Caller struct {
|
||||||
|
Type string // Authentication type for the action request. One of 'basic', 'operator', 'signature' or 'token'
|
||||||
|
User string // When the authentication type is 'basic', this member will be present and include user login
|
||||||
|
IP string // The IP addresses this from which the action was requested. Not present if type is 'operator'
|
||||||
|
KeyId string // When authentication type is either 'signature' or 'token', SSH key identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendJSON marshals the given attribute value and appends it as an encoded value to the given json data.
|
||||||
|
// The newly encode (attr, value) is inserted just before the closing "}" in the json data.
|
||||||
|
func appendJSON(data []byte, attr string, value interface{}) ([]byte, error) {
|
||||||
|
newData, err := json.Marshal(&value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
strData := string(data)
|
||||||
|
result := fmt.Sprintf(`%s, "%s":%s}`, strData[:len(strData)-1], attr, string(newData))
|
||||||
|
return []byte(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonOpts CreateMachineOpts
|
||||||
|
|
||||||
|
// MarshalJSON turns the given CreateMachineOpts into JSON
|
||||||
|
func (opts CreateMachineOpts) MarshalJSON() ([]byte, error) {
|
||||||
|
jo := jsonOpts(opts)
|
||||||
|
data, err := json.Marshal(&jo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for k, v := range opts.Tags {
|
||||||
|
if !strings.HasPrefix(k, "tag.") {
|
||||||
|
k = "tag." + k
|
||||||
|
}
|
||||||
|
data, err = appendJSON(data, k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range opts.Metadata {
|
||||||
|
if !strings.HasPrefix(k, "metadata.") {
|
||||||
|
k = "metadata." + k
|
||||||
|
}
|
||||||
|
data, err = appendJSON(data, k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMachines lists all machines on record for an account.
|
||||||
|
// You can paginate this API by passing in offset, and limit
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines
|
||||||
|
func (c *Client) ListMachines(filter *Filter) ([]Machine, error) {
|
||||||
|
var resp []Machine
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiMachines,
|
||||||
|
filter: filter,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of machines")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountMachines returns the number of machines on record for an account.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines
|
||||||
|
func (c *Client) CountMachines() (int, error) {
|
||||||
|
var resp int
|
||||||
|
req := request{
|
||||||
|
method: client.HEAD,
|
||||||
|
url: apiMachines,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return -1, errors.Newf(err, "failed to get count of machines")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMachine returns the machine specified by machineId.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachine
|
||||||
|
func (c *Client) GetMachine(machineID string) (*Machine, error) {
|
||||||
|
var resp Machine
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMachine creates a new machine with the options specified.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachine
|
||||||
|
func (c *Client) CreateMachine(opts CreateMachineOpts) (*Machine, error) {
|
||||||
|
var resp Machine
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: apiMachines,
|
||||||
|
reqValue: opts,
|
||||||
|
resp: &resp,
|
||||||
|
expectedStatus: http.StatusCreated,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to create machine with name: %s", opts.Name)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopMachine stops a running machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#StopMachine
|
||||||
|
func (c *Client) StopMachine(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionStop),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to stop machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartMachine starts a stopped machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#StartMachine
|
||||||
|
func (c *Client) StartMachine(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionStart),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to start machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebootMachine reboots (stop followed by a start) a machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#RebootMachine
|
||||||
|
func (c *Client) RebootMachine(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionReboot),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to reboot machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeMachine allows you to resize a SmartMachine. Virtual machines can also
|
||||||
|
// be resized, but only resizing virtual machines to a higher capacity package
|
||||||
|
// is supported.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ResizeMachine
|
||||||
|
func (c *Client) ResizeMachine(machineID, packageName string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s&package=%s", apiMachines, machineID, actionResize, packageName),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to resize machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenameMachine renames an existing machine.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#RenameMachine
|
||||||
|
func (c *Client) RenameMachine(machineID, machineName string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.POST,
|
||||||
|
url: fmt.Sprintf("%s/%s?action=%s&name=%s", apiMachines, machineID, actionRename, machineName),
|
||||||
|
expectedStatus: http.StatusAccepted,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to rename machine with id: %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMachine allows you to completely destroy a machine. Machine must be in the 'stopped' state.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachine
|
||||||
|
func (c *Client) DeleteMachine(machineID string) error {
|
||||||
|
req := request{
|
||||||
|
method: client.DELETE,
|
||||||
|
url: makeURL(apiMachines, machineID),
|
||||||
|
expectedStatus: http.StatusNoContent,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return errors.Newf(err, "failed to delete machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MachineAudit provides a list of machine's accomplished actions, (sorted from
|
||||||
|
// latest to older one).
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#MachineAudit
|
||||||
|
func (c *Client) MachineAudit(machineID string) ([]AuditAction, error) {
|
||||||
|
var resp []AuditAction
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiMachines, machineID, apiAudit),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get actions for machine with id %s", machineID)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Network represents a network available to a given account
|
||||||
|
type Network struct {
|
||||||
|
Id string // Unique identifier for the network
|
||||||
|
Name string // Network name
|
||||||
|
Public bool // Whether this a public or private (rfc1918) network
|
||||||
|
Description string // Optional description for this network, when name is not enough
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNetworks lists all the networks which can be used by the given account.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListNetworks
|
||||||
|
func (c *Client) ListNetworks() ([]Network, error) {
|
||||||
|
var resp []Network
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiNetworks,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of networks")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetwork retrieves an individual network record.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetNetwork
|
||||||
|
func (c *Client) GetNetwork(networkID string) (*Network, error) {
|
||||||
|
var resp Network
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiNetworks, networkID),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get network with id %s", networkID)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package represents a named collections of resources that are used to describe the 'sizes'
|
||||||
|
// of either a smart machine or a virtual machine.
|
||||||
|
type Package struct {
|
||||||
|
Name string // Name for the package
|
||||||
|
Memory int // Memory available (in Mb)
|
||||||
|
Disk int // Disk space available (in Gb)
|
||||||
|
Swap int // Swap memory available (in Mb)
|
||||||
|
VCPUs int // Number of VCPUs for the package
|
||||||
|
Default bool // Indicates whether this is the default package in the datacenter
|
||||||
|
Id string // Unique identifier for the package
|
||||||
|
Version string // Version for the package
|
||||||
|
Group string // Group this package belongs to
|
||||||
|
Description string // Human friendly description for the package
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPackages provides a list of packages available in the datacenter.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#ListPackages
|
||||||
|
func (c *Client) ListPackages(filter *Filter) ([]Package, error) {
|
||||||
|
var resp []Package
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiPackages,
|
||||||
|
filter: filter,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of packages")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPackage returns the package specified by packageName. NOTE: packageName can
|
||||||
|
// specify either the package name or package ID.
|
||||||
|
// See API docs: http://apidocs.joyent.com/cloudapi/#GetPackage
|
||||||
|
func (c *Client) GetPackage(packageName string) (*Package, error) {
|
||||||
|
var resp Package
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: makeURL(apiPackages, packageName),
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get package with name: %s", packageName)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package cloudapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/joyent/gocommon/client"
|
||||||
|
"github.com/joyent/gocommon/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// list available services
|
||||||
|
func (c *Client) ListServices() (map[string]string, error) {
|
||||||
|
var resp map[string]string
|
||||||
|
req := request{
|
||||||
|
method: client.GET,
|
||||||
|
url: apiServices,
|
||||||
|
resp: &resp,
|
||||||
|
}
|
||||||
|
if _, err := c.sendRequest(req); err != nil {
|
||||||
|
return nil, errors.Newf(err, "failed to get list of services")
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
|
@ -0,0 +1,373 @@
|
||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,135 @@
|
||||||
|
//
|
||||||
|
// gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Joyent Inc.
|
||||||
|
//
|
||||||
|
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
|
||||||
|
//
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Authorization Headers
|
||||||
|
SdcSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
|
||||||
|
MantaSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\",signature=\"%s\""
|
||||||
|
)
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
User string
|
||||||
|
PrivateKey PrivateKey
|
||||||
|
Algorithm string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
UserAuthentication *Auth
|
||||||
|
SdcKeyId string
|
||||||
|
SdcEndpoint Endpoint
|
||||||
|
MantaKeyId string
|
||||||
|
MantaEndpoint Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivateKey struct {
|
||||||
|
key *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuth creates a new Auth.
|
||||||
|
func NewAuth(user, privateKey, algorithm string) (*Auth, error) {
|
||||||
|
block, _ := pem.Decode([]byte(privateKey))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("invalid private key data: %s", privateKey)
|
||||||
|
}
|
||||||
|
rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("An error occurred while parsing the key: %s", err)
|
||||||
|
}
|
||||||
|
return &Auth{user, PrivateKey{rsakey}, algorithm}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CreateAuthorizationHeader returns the Authorization header for the give request.
|
||||||
|
func CreateAuthorizationHeader(headers http.Header, credentials *Credentials, isMantaRequest bool) (string, error) {
|
||||||
|
if isMantaRequest {
|
||||||
|
signature, err := GetSignature(credentials.UserAuthentication, "date: "+headers.Get("Date"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(MantaSignature, credentials.UserAuthentication.User, credentials.MantaKeyId,
|
||||||
|
credentials.UserAuthentication.Algorithm, signature), nil
|
||||||
|
}
|
||||||
|
signature, err := GetSignature(credentials.UserAuthentication, headers.Get("Date"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(SdcSignature, credentials.UserAuthentication.User, credentials.SdcKeyId,
|
||||||
|
credentials.UserAuthentication.Algorithm, signature), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The GetSignature method signs the specified key according to http://apidocs.joyent.com/cloudapi/#issuing-requests
|
||||||
|
// and http://apidocs.joyent.com/manta/api.html#authentication.
|
||||||
|
func GetSignature(auth *Auth, signing string) (string, error) {
|
||||||
|
hashFunc := getHashFunction(auth.Algorithm)
|
||||||
|
hash := hashFunc.New()
|
||||||
|
hash.Write([]byte(signing))
|
||||||
|
|
||||||
|
digest := hash.Sum(nil)
|
||||||
|
|
||||||
|
signed, err := rsa.SignPKCS1v15(rand.Reader, auth.PrivateKey.key, hashFunc, digest)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("An error occurred while signing the key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(signed), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to get the Hash function based on the algorithm
|
||||||
|
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
|
||||||
|
switch strings.ToLower(algorithm) {
|
||||||
|
case "rsa-sha1":
|
||||||
|
hashFunc = crypto.SHA1
|
||||||
|
case "rsa-sha224", "rsa-sha256":
|
||||||
|
hashFunc = crypto.SHA256
|
||||||
|
case "rsa-sha384", "rsa-sha512":
|
||||||
|
hashFunc = crypto.SHA512
|
||||||
|
default:
|
||||||
|
hashFunc = crypto.SHA256
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cred *Credentials) Region() string {
|
||||||
|
sdcUrl := cred.SdcEndpoint.URL
|
||||||
|
|
||||||
|
if isLocalhost(sdcUrl) {
|
||||||
|
return "some-region"
|
||||||
|
}
|
||||||
|
return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")]
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLocalhost(u string) bool {
|
||||||
|
parsedUrl, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.1") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
Loading…
Reference in New Issue