Bump deps
This commit is contained in:
parent
6b5327a04d
commit
ddb9a0ce52
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -301,6 +302,65 @@ const (
|
||||||
ContainerAccessTypeContainer ContainerAccessType = "container"
|
ContainerAccessTypeContainer ContainerAccessType = "container"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ContainerAccessOptions are used when setting ACLs of containers (after creation)
|
||||||
|
type ContainerAccessOptions struct {
|
||||||
|
ContainerAccess ContainerAccessType
|
||||||
|
Timeout int
|
||||||
|
LeaseID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessPolicyDetails are used for SETTING policies
|
||||||
|
type AccessPolicyDetails struct {
|
||||||
|
ID string
|
||||||
|
StartTime time.Time
|
||||||
|
ExpiryTime time.Time
|
||||||
|
CanRead bool
|
||||||
|
CanWrite bool
|
||||||
|
CanDelete bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerPermissions is used when setting permissions and Access Policies for containers.
|
||||||
|
type ContainerPermissions struct {
|
||||||
|
AccessOptions ContainerAccessOptions
|
||||||
|
AccessPolicy AccessPolicyDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessPolicyDetailsXML has specifics about an access policy
|
||||||
|
// annotated with XML details.
|
||||||
|
type AccessPolicyDetailsXML struct {
|
||||||
|
StartTime time.Time `xml:"Start"`
|
||||||
|
ExpiryTime time.Time `xml:"Expiry"`
|
||||||
|
Permission string `xml:"Permission"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedIdentifier is a wrapper for a specific policy
|
||||||
|
type SignedIdentifier struct {
|
||||||
|
ID string `xml:"Id"`
|
||||||
|
AccessPolicy AccessPolicyDetailsXML `xml:"AccessPolicy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedIdentifiers part of the response from GetPermissions call.
|
||||||
|
type SignedIdentifiers struct {
|
||||||
|
SignedIdentifiers []SignedIdentifier `xml:"SignedIdentifier"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessPolicy is the response type from the GetPermissions call.
|
||||||
|
type AccessPolicy struct {
|
||||||
|
SignedIdentifiersList SignedIdentifiers `xml:"SignedIdentifiers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAccessResponse is returned for the GetContainerPermissions function.
|
||||||
|
// This contains both the permission and access policy for the container.
|
||||||
|
type ContainerAccessResponse struct {
|
||||||
|
ContainerAccess ContainerAccessType
|
||||||
|
AccessPolicy SignedIdentifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAccessHeader references header used when setting/getting container ACL
|
||||||
|
const (
|
||||||
|
ContainerAccessHeader string = "x-ms-blob-public-access"
|
||||||
|
)
|
||||||
|
|
||||||
// Maximum sizes (per REST API) for various concepts
|
// Maximum sizes (per REST API) for various concepts
|
||||||
const (
|
const (
|
||||||
MaxBlobBlockSize = 4 * 1024 * 1024
|
MaxBlobBlockSize = 4 * 1024 * 1024
|
||||||
|
@ -416,7 +476,7 @@ func (b BlobStorageClient) createContainer(name string, access ContainerAccessTy
|
||||||
|
|
||||||
headers := b.client.getStandardHeaders()
|
headers := b.client.getStandardHeaders()
|
||||||
if access != "" {
|
if access != "" {
|
||||||
headers["x-ms-blob-public-access"] = string(access)
|
headers[ContainerAccessHeader] = string(access)
|
||||||
}
|
}
|
||||||
return b.client.exec(verb, uri, headers, nil)
|
return b.client.exec(verb, uri, headers, nil)
|
||||||
}
|
}
|
||||||
|
@ -438,6 +498,101 @@ func (b BlobStorageClient) ContainerExists(name string) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetContainerPermissions sets up container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179391.aspx
|
||||||
|
func (b BlobStorageClient) SetContainerPermissions(container string, containerPermissions ContainerPermissions) (err error) {
|
||||||
|
params := url.Values{
|
||||||
|
"restype": {"container"},
|
||||||
|
"comp": {"acl"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerPermissions.AccessOptions.Timeout > 0 {
|
||||||
|
params.Add("timeout", strconv.Itoa(containerPermissions.AccessOptions.Timeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := b.client.getEndpoint(blobServiceName, pathForContainer(container), params)
|
||||||
|
headers := b.client.getStandardHeaders()
|
||||||
|
if containerPermissions.AccessOptions.ContainerAccess != "" {
|
||||||
|
headers[ContainerAccessHeader] = string(containerPermissions.AccessOptions.ContainerAccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerPermissions.AccessOptions.LeaseID != "" {
|
||||||
|
headers[leaseID] = containerPermissions.AccessOptions.LeaseID
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the XML for the SharedAccessSignature if required.
|
||||||
|
accessPolicyXML, err := generateAccessPolicy(containerPermissions.AccessPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *storageResponse
|
||||||
|
if accessPolicyXML != "" {
|
||||||
|
headers["Content-Length"] = strconv.Itoa(len(accessPolicyXML))
|
||||||
|
resp, err = b.client.exec("PUT", uri, headers, strings.NewReader(accessPolicyXML))
|
||||||
|
} else {
|
||||||
|
resp, err = b.client.exec("PUT", uri, headers, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
defer func() {
|
||||||
|
err = resp.body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if resp.statusCode != http.StatusOK {
|
||||||
|
return errors.New("Unable to set permissions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainerPermissions gets the container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179469.aspx
|
||||||
|
// If timeout is 0 then it will not be passed to Azure
|
||||||
|
// leaseID will only be passed to Azure if populated
|
||||||
|
// Returns permissionResponse which is combined permissions and AccessPolicy
|
||||||
|
func (b BlobStorageClient) GetContainerPermissions(container string, timeout int, leaseID string) (permissionResponse *ContainerAccessResponse, err error) {
|
||||||
|
params := url.Values{"restype": {"container"},
|
||||||
|
"comp": {"acl"}}
|
||||||
|
|
||||||
|
if timeout > 0 {
|
||||||
|
params.Add("timeout", strconv.Itoa(timeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := b.client.getEndpoint(blobServiceName, pathForContainer(container), params)
|
||||||
|
headers := b.client.getStandardHeaders()
|
||||||
|
|
||||||
|
if leaseID != "" {
|
||||||
|
headers[leaseID] = leaseID
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := b.client.exec("GET", uri, headers, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// containerAccess. Blob, Container, empty
|
||||||
|
containerAccess := resp.headers.Get(http.CanonicalHeaderKey(ContainerAccessHeader))
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = resp.body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var out AccessPolicy
|
||||||
|
err = xmlUnmarshal(resp.body, &out.SignedIdentifiersList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionResponse = &ContainerAccessResponse{}
|
||||||
|
permissionResponse.AccessPolicy = out.SignedIdentifiersList
|
||||||
|
permissionResponse.ContainerAccess = ContainerAccessType(containerAccess)
|
||||||
|
|
||||||
|
return permissionResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteContainer deletes the container with given name on the storage
|
// DeleteContainer deletes the container with given name on the storage
|
||||||
// account. If the container does not exist returns error.
|
// account. If the container does not exist returns error.
|
||||||
//
|
//
|
||||||
|
@ -614,8 +769,6 @@ func (b BlobStorageClient) AcquireLease(container string, name string, leaseTime
|
||||||
return returnedLeaseID, nil
|
return returnedLeaseID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// what should we return in case of HTTP 201 but no lease ID?
|
|
||||||
// or it just cant happen? (brave words)
|
|
||||||
return "", errors.New("LeaseID not returned")
|
return "", errors.New("LeaseID not returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1106,15 +1259,20 @@ func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, ext
|
||||||
//
|
//
|
||||||
// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
|
// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
|
||||||
func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error {
|
func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error {
|
||||||
copyID, err := b.startBlobCopy(container, name, sourceBlob)
|
copyID, err := b.StartBlobCopy(container, name, sourceBlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.waitForBlobCopy(container, name, copyID)
|
return b.WaitForBlobCopy(container, name, copyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (string, error) {
|
// StartBlobCopy starts a blob copy operation.
|
||||||
|
// sourceBlob parameter must be a canonical URL to the blob (can be
|
||||||
|
// obtained using GetBlobURL method.)
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
|
||||||
|
func (b BlobStorageClient) StartBlobCopy(container, name, sourceBlob string) (string, error) {
|
||||||
uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
|
uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
|
||||||
|
|
||||||
headers := b.client.getStandardHeaders()
|
headers := b.client.getStandardHeaders()
|
||||||
|
@ -1137,7 +1295,39 @@ func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (st
|
||||||
return copyID, nil
|
return copyID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BlobStorageClient) waitForBlobCopy(container, name, copyID string) error {
|
// AbortBlobCopy aborts a BlobCopy which has already been triggered by the StartBlobCopy function.
|
||||||
|
// copyID is generated from StartBlobCopy function.
|
||||||
|
// currentLeaseID is required IF the destination blob has an active lease on it.
|
||||||
|
// As defined in https://msdn.microsoft.com/en-us/library/azure/jj159098.aspx
|
||||||
|
func (b BlobStorageClient) AbortBlobCopy(container, name, copyID, currentLeaseID string, timeout int) error {
|
||||||
|
params := url.Values{"comp": {"copy"}, "copyid": {copyID}}
|
||||||
|
if timeout > 0 {
|
||||||
|
params.Add("timeout", strconv.Itoa(timeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
|
||||||
|
headers := b.client.getStandardHeaders()
|
||||||
|
headers["x-ms-copy-action"] = "abort"
|
||||||
|
|
||||||
|
if currentLeaseID != "" {
|
||||||
|
headers[leaseID] = currentLeaseID
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := b.client.exec("PUT", uri, headers, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.body.Close()
|
||||||
|
|
||||||
|
if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForBlobCopy loops until a BlobCopy operation is completed (or fails with error)
|
||||||
|
func (b BlobStorageClient) WaitForBlobCopy(container, name, copyID string) error {
|
||||||
for {
|
for {
|
||||||
props, err := b.GetBlobProperties(container, name)
|
props, err := b.GetBlobProperties(container, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1181,10 +1371,12 @@ func (b BlobStorageClient) DeleteBlob(container, name string, extraHeaders map[s
|
||||||
// See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx
|
// See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx
|
||||||
func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) {
|
func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) {
|
||||||
resp, err := b.deleteBlob(container, name, extraHeaders)
|
resp, err := b.deleteBlob(container, name, extraHeaders)
|
||||||
if resp != nil && (resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound) {
|
if resp != nil {
|
||||||
return resp.statusCode == http.StatusAccepted, nil
|
defer resp.body.Close()
|
||||||
|
if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
|
||||||
|
return resp.statusCode == http.StatusAccepted, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer resp.body.Close()
|
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1210,17 +1402,18 @@ func pathForBlob(container, name string) string {
|
||||||
return fmt.Sprintf("/%s/%s", container, name)
|
return fmt.Sprintf("/%s/%s", container, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlobSASURI creates an URL to the specified blob which contains the Shared
|
// GetBlobSASURIWithSignedIPAndProtocol creates an URL to the specified blob which contains the Shared
|
||||||
// Access Signature with specified permissions and expiration time.
|
// Access Signature with specified permissions and expiration time. Also includes signedIPRange and allowed procotols.
|
||||||
|
// If old API version is used but no signedIP is passed (ie empty string) then this should still work.
|
||||||
|
// We only populate the signedIP when it non-empty.
|
||||||
//
|
//
|
||||||
// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
|
// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
|
||||||
func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) {
|
func (b BlobStorageClient) GetBlobSASURIWithSignedIPAndProtocol(container, name string, expiry time.Time, permissions string, signedIPRange string, HTTPSOnly bool) (string, error) {
|
||||||
var (
|
var (
|
||||||
signedPermissions = permissions
|
signedPermissions = permissions
|
||||||
blobURL = b.GetBlobURL(container, name)
|
blobURL = b.GetBlobURL(container, name)
|
||||||
)
|
)
|
||||||
canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL)
|
canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -1232,7 +1425,6 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
||||||
|
|
||||||
// We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component).
|
// We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component).
|
||||||
canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
|
canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
|
||||||
|
|
||||||
canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
|
canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -1241,7 +1433,11 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
||||||
signedExpiry := expiry.UTC().Format(time.RFC3339)
|
signedExpiry := expiry.UTC().Format(time.RFC3339)
|
||||||
signedResource := "b"
|
signedResource := "b"
|
||||||
|
|
||||||
stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions)
|
protocols := "https,http"
|
||||||
|
if HTTPSOnly {
|
||||||
|
protocols = "https"
|
||||||
|
}
|
||||||
|
stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions, signedIPRange, protocols)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -1255,6 +1451,13 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
||||||
"sig": {sig},
|
"sig": {sig},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.client.apiVersion >= "2015-04-05" {
|
||||||
|
sasParams.Add("spr", protocols)
|
||||||
|
if signedIPRange != "" {
|
||||||
|
sasParams.Add("sip", signedIPRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sasURL, err := url.Parse(blobURL)
|
sasURL, err := url.Parse(blobURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -1263,16 +1466,89 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
||||||
return sasURL.String(), nil
|
return sasURL.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string) (string, error) {
|
// GetBlobSASURI creates an URL to the specified blob which contains the Shared
|
||||||
|
// Access Signature with specified permissions and expiration time.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
|
||||||
|
func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) {
|
||||||
|
url, err := b.GetBlobSASURIWithSignedIPAndProtocol(container, name, expiry, permissions, "", false)
|
||||||
|
return url, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string, signedIP string, protocols string) (string, error) {
|
||||||
var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string
|
var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string
|
||||||
|
|
||||||
if signedVersion >= "2015-02-21" {
|
if signedVersion >= "2015-02-21" {
|
||||||
canonicalizedResource = "/blob" + canonicalizedResource
|
canonicalizedResource = "/blob" + canonicalizedResource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
|
||||||
|
if signedVersion >= "2015-04-05" {
|
||||||
|
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
|
||||||
|
}
|
||||||
|
|
||||||
// reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
|
// reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
|
||||||
if signedVersion >= "2013-08-15" {
|
if signedVersion >= "2013-08-15" {
|
||||||
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
|
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
|
return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generatePermissions(accessPolicy AccessPolicyDetails) (permissions string) {
|
||||||
|
// generate the permissions string (rwd).
|
||||||
|
// still want the end user API to have bool flags.
|
||||||
|
permissions = ""
|
||||||
|
|
||||||
|
if accessPolicy.CanRead {
|
||||||
|
permissions += "r"
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessPolicy.CanWrite {
|
||||||
|
permissions += "w"
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessPolicy.CanDelete {
|
||||||
|
permissions += "d"
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertAccessPolicyToXMLStructs converts between AccessPolicyDetails which is a struct better for API usage to the
|
||||||
|
// AccessPolicy struct which will get converted to XML.
|
||||||
|
func convertAccessPolicyToXMLStructs(accessPolicy AccessPolicyDetails) SignedIdentifiers {
|
||||||
|
return SignedIdentifiers{
|
||||||
|
SignedIdentifiers: []SignedIdentifier{
|
||||||
|
{
|
||||||
|
ID: accessPolicy.ID,
|
||||||
|
AccessPolicy: AccessPolicyDetailsXML{
|
||||||
|
StartTime: accessPolicy.StartTime.UTC().Round(time.Second),
|
||||||
|
ExpiryTime: accessPolicy.ExpiryTime.UTC().Round(time.Second),
|
||||||
|
Permission: generatePermissions(accessPolicy),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateAccessPolicy generates the XML access policy used as the payload for SetContainerPermissions.
|
||||||
|
func generateAccessPolicy(accessPolicy AccessPolicyDetails) (accessPolicyXML string, err error) {
|
||||||
|
|
||||||
|
if accessPolicy.ID != "" {
|
||||||
|
signedIdentifiers := convertAccessPolicyToXMLStructs(accessPolicy)
|
||||||
|
body, _, err := xmlMarshal(signedIdentifiers)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlByteArray, err := ioutil.ReadAll(body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
accessPolicyXML = string(xmlByteArray)
|
||||||
|
return accessPolicyXML, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
|
@ -305,7 +305,7 @@ func (c Client) buildCanonicalizedResourceTable(uri string) (string, error) {
|
||||||
cr := "/" + c.getCanonicalizedAccountName()
|
cr := "/" + c.getCanonicalizedAccountName()
|
||||||
|
|
||||||
if len(u.Path) > 0 {
|
if len(u.Path) > 0 {
|
||||||
cr += u.Path
|
cr += u.EscapedPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
return cr, nil
|
return cr, nil
|
||||||
|
|
|
@ -82,6 +82,24 @@ func (p PeekMessagesParameters) getParameters() url.Values {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateMessageParameters is the set of options can be specified for Update Messsage
|
||||||
|
// operation. A zero struct does not use any preferences for the request.
|
||||||
|
type UpdateMessageParameters struct {
|
||||||
|
PopReceipt string
|
||||||
|
VisibilityTimeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p UpdateMessageParameters) getParameters() url.Values {
|
||||||
|
out := url.Values{}
|
||||||
|
if p.PopReceipt != "" {
|
||||||
|
out.Set("popreceipt", p.PopReceipt)
|
||||||
|
}
|
||||||
|
if p.VisibilityTimeout != 0 {
|
||||||
|
out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// GetMessagesResponse represents a response returned from Get Messages
|
// GetMessagesResponse represents a response returned from Get Messages
|
||||||
// operation.
|
// operation.
|
||||||
type GetMessagesResponse struct {
|
type GetMessagesResponse struct {
|
||||||
|
@ -304,3 +322,23 @@ func (c QueueServiceClient) DeleteMessage(queue, messageID, popReceipt string) e
|
||||||
defer resp.body.Close()
|
defer resp.body.Close()
|
||||||
return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
|
return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateMessage operation deletes the specified message.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/azure/hh452234.aspx
|
||||||
|
func (c QueueServiceClient) UpdateMessage(queue string, messageID string, message string, params UpdateMessageParameters) error {
|
||||||
|
uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), params.getParameters())
|
||||||
|
req := putMessageRequest{MessageText: message}
|
||||||
|
body, nn, err := xmlMarshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
headers := c.client.getStandardHeaders()
|
||||||
|
headers["Content-Length"] = fmt.Sprintf("%d", nn)
|
||||||
|
resp, err := c.client.exec("PUT", uri, headers, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.body.Close()
|
||||||
|
return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
|
||||||
|
}
|
||||||
|
|
|
@ -31,8 +31,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrOutOfBounds - Index out of bounds.
|
// ErrOutOfBounds - Index out of bounds.
|
||||||
|
@ -63,40 +62,30 @@ var (
|
||||||
ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
|
ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
|
||||||
)
|
)
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
// Container - an internal structure that holds a reference to the core interface map of the parsed
|
||||||
Container - an internal structure that holds a reference to the core interface map of the parsed
|
// json. Use this container to move context.
|
||||||
json. Use this container to move context.
|
|
||||||
*/
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
object interface{}
|
object interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Data - Return the contained data as an interface{}.
|
||||||
Data - Return the contained data as an interface{}.
|
|
||||||
*/
|
|
||||||
func (g *Container) Data() interface{} {
|
func (g *Container) Data() interface{} {
|
||||||
return g.object
|
return g.object
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
// Path - Search for a value using dot notation.
|
||||||
Path - Search for a value using dot notation.
|
|
||||||
*/
|
|
||||||
func (g *Container) Path(path string) *Container {
|
func (g *Container) Path(path string) *Container {
|
||||||
return g.Search(strings.Split(path, ".")...)
|
return g.Search(strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Search - Attempt to find and return an object within the JSON structure by specifying the
|
||||||
Search - Attempt to find and return an object within the JSON structure by specifying the hierarchy
|
// hierarchy of field names to locate the target. If the search encounters an array and has not
|
||||||
of field names to locate the target. If the search encounters an array and has not reached the end
|
// reached the end target then it will iterate each object of the array for the target and return
|
||||||
target then it will iterate each object of the array for the target and return all of the results in
|
// all of the results in a JSON array.
|
||||||
a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) Search(hierarchy ...string) *Container {
|
func (g *Container) Search(hierarchy ...string) *Container {
|
||||||
var object interface{}
|
var object interface{}
|
||||||
|
|
||||||
|
@ -124,31 +113,22 @@ func (g *Container) Search(hierarchy ...string) *Container {
|
||||||
return &Container{object}
|
return &Container{object}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// S - Shorthand method, does the same thing as Search.
|
||||||
S - Shorthand method, does the same thing as Search.
|
|
||||||
*/
|
|
||||||
func (g *Container) S(hierarchy ...string) *Container {
|
func (g *Container) S(hierarchy ...string) *Container {
|
||||||
return g.Search(hierarchy...)
|
return g.Search(hierarchy...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Exists - Checks whether a path exists.
|
||||||
Exists - Checks whether a path exists.
|
|
||||||
*/
|
|
||||||
func (g *Container) Exists(hierarchy ...string) bool {
|
func (g *Container) Exists(hierarchy ...string) bool {
|
||||||
return g.Search(hierarchy...).Data() != nil
|
return g.Search(hierarchy...).Data() != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ExistsP - Checks whether a dot notation path exists.
|
||||||
ExistsP - Checks whether a dot notation path exists.
|
|
||||||
*/
|
|
||||||
func (g *Container) ExistsP(path string) bool {
|
func (g *Container) ExistsP(path string) bool {
|
||||||
return g.Exists(strings.Split(path, ".")...)
|
return g.Exists(strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Index - Attempt to find and return an object within a JSON array by index.
|
||||||
Index - Attempt to find and return an object with a JSON array by specifying the index of the
|
|
||||||
target.
|
|
||||||
*/
|
|
||||||
func (g *Container) Index(index int) *Container {
|
func (g *Container) Index(index int) *Container {
|
||||||
if array, ok := g.Data().([]interface{}); ok {
|
if array, ok := g.Data().([]interface{}); ok {
|
||||||
if index >= len(array) {
|
if index >= len(array) {
|
||||||
|
@ -159,11 +139,9 @@ func (g *Container) Index(index int) *Container {
|
||||||
return &Container{nil}
|
return &Container{nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Children - Return a slice of all the children of the array. This also works for objects, however,
|
||||||
Children - Return a slice of all the children of the array. This also works for objects, however,
|
// the children returned for an object will NOT be in order and you lose the names of the returned
|
||||||
the children returned for an object will NOT be in order and you lose the names of the returned
|
// objects this way.
|
||||||
objects this way.
|
|
||||||
*/
|
|
||||||
func (g *Container) Children() ([]*Container, error) {
|
func (g *Container) Children() ([]*Container, error) {
|
||||||
if array, ok := g.Data().([]interface{}); ok {
|
if array, ok := g.Data().([]interface{}); ok {
|
||||||
children := make([]*Container, len(array))
|
children := make([]*Container, len(array))
|
||||||
|
@ -182,9 +160,7 @@ func (g *Container) Children() ([]*Container, error) {
|
||||||
return nil, ErrNotObjOrArray
|
return nil, ErrNotObjOrArray
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ChildrenMap - Return a map of all the children of an object.
|
||||||
ChildrenMap - Return a map of all the children of an object.
|
|
||||||
*/
|
|
||||||
func (g *Container) ChildrenMap() (map[string]*Container, error) {
|
func (g *Container) ChildrenMap() (map[string]*Container, error) {
|
||||||
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
||||||
children := map[string]*Container{}
|
children := map[string]*Container{}
|
||||||
|
@ -196,14 +172,11 @@ func (g *Container) ChildrenMap() (map[string]*Container, error) {
|
||||||
return nil, ErrNotObj
|
return nil, ErrNotObj
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
// Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be
|
||||||
Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be
|
// constructed, and if a collision occurs with a non object type whilst iterating the path an error
|
||||||
constructed, and if a collision occurs with a non object type whilst iterating the path an error is
|
// is returned.
|
||||||
returned.
|
|
||||||
*/
|
|
||||||
func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
|
func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
g.object = value
|
g.object = value
|
||||||
|
@ -229,16 +202,12 @@ func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
|
||||||
return &Container{object}, nil
|
return &Container{object}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// SetP - Does the same as Set, but using a dot notation JSON path.
|
||||||
SetP - Does the same as Set, but using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) SetP(value interface{}, path string) (*Container, error) {
|
func (g *Container) SetP(value interface{}, path string) (*Container, error) {
|
||||||
return g.Set(value, strings.Split(path, ".")...)
|
return g.Set(value, strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// SetIndex - Set a value of an array element based on the index.
|
||||||
SetIndex - Set a value of an array element based on the index.
|
|
||||||
*/
|
|
||||||
func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
|
func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
|
||||||
if array, ok := g.Data().([]interface{}); ok {
|
if array, ok := g.Data().([]interface{}); ok {
|
||||||
if index >= len(array) {
|
if index >= len(array) {
|
||||||
|
@ -250,80 +219,60 @@ func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
|
||||||
return &Container{nil}, ErrNotArray
|
return &Container{nil}, ErrNotArray
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Object - Create a new JSON object at a path. Returns an error if the path contains a collision
|
||||||
Object - Create a new JSON object at a path. Returns an error if the path contains a collision with
|
// with a non object type.
|
||||||
a non object type.
|
|
||||||
*/
|
|
||||||
func (g *Container) Object(path ...string) (*Container, error) {
|
func (g *Container) Object(path ...string) (*Container, error) {
|
||||||
return g.Set(map[string]interface{}{}, path...)
|
return g.Set(map[string]interface{}{}, path...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ObjectP - Does the same as Object, but using a dot notation JSON path.
|
||||||
ObjectP - Does the same as Object, but using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ObjectP(path string) (*Container, error) {
|
func (g *Container) ObjectP(path string) (*Container, error) {
|
||||||
return g.Object(strings.Split(path, ".")...)
|
return g.Object(strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an
|
||||||
ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an array
|
// array or the index is out of bounds.
|
||||||
or the index is out of bounds.
|
|
||||||
*/
|
|
||||||
func (g *Container) ObjectI(index int) (*Container, error) {
|
func (g *Container) ObjectI(index int) (*Container, error) {
|
||||||
return g.SetIndex(map[string]interface{}{}, index)
|
return g.SetIndex(map[string]interface{}{}, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Array - Create a new JSON array at a path. Returns an error if the path contains a collision with
|
||||||
Array - Create a new JSON array at a path. Returns an error if the path contains a collision with
|
// a non object type.
|
||||||
a non object type.
|
|
||||||
*/
|
|
||||||
func (g *Container) Array(path ...string) (*Container, error) {
|
func (g *Container) Array(path ...string) (*Container, error) {
|
||||||
return g.Set([]interface{}{}, path...)
|
return g.Set([]interface{}{}, path...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayP - Does the same as Array, but using a dot notation JSON path.
|
||||||
ArrayP - Does the same as Array, but using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayP(path string) (*Container, error) {
|
func (g *Container) ArrayP(path string) (*Container, error) {
|
||||||
return g.Array(strings.Split(path, ".")...)
|
return g.Array(strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an
|
||||||
ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an array
|
// array or the index is out of bounds.
|
||||||
or the index is out of bounds.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayI(index int) (*Container, error) {
|
func (g *Container) ArrayI(index int) (*Container, error) {
|
||||||
return g.SetIndex([]interface{}{}, index)
|
return g.SetIndex([]interface{}{}, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the
|
||||||
ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the path
|
// path contains a collision with a non object type.
|
||||||
contains a collision with a non object type.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) {
|
func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) {
|
||||||
a := make([]interface{}, size)
|
a := make([]interface{}, size)
|
||||||
return g.Set(a, path...)
|
return g.Set(a, path...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path.
|
||||||
ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) {
|
func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) {
|
||||||
return g.ArrayOfSize(size, strings.Split(path, ".")...)
|
return g.ArrayOfSize(size, strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error
|
||||||
ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error if
|
// if the object is not an array or the index is out of bounds.
|
||||||
the object is not an array or the index is out of bounds.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) {
|
func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) {
|
||||||
a := make([]interface{}, size)
|
a := make([]interface{}, size)
|
||||||
return g.SetIndex(a, index)
|
return g.SetIndex(a, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Delete - Delete an element at a JSON path, an error is returned if the element does not exist.
|
||||||
Delete - Delete an element at a JSON path, an error is returned if the element does not exist.
|
|
||||||
*/
|
|
||||||
func (g *Container) Delete(path ...string) error {
|
func (g *Container) Delete(path ...string) error {
|
||||||
var object interface{}
|
var object interface{}
|
||||||
|
|
||||||
|
@ -346,24 +295,19 @@ func (g *Container) Delete(path ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// DeleteP - Does the same as Delete, but using a dot notation JSON path.
|
||||||
DeleteP - Does the same as Delete, but using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) DeleteP(path string) error {
|
func (g *Container) DeleteP(path string) error {
|
||||||
return g.Delete(strings.Split(path, ".")...)
|
return g.Delete(strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Array modification/search - Keeping these options simple right now, no need for anything more
|
Array modification/search - Keeping these options simple right now, no need for anything more
|
||||||
complicated since you can just cast to []interface{}, modify and then reassign with Set.
|
complicated since you can just cast to []interface{}, modify and then reassign with Set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
// ArrayAppend - Append a value onto a JSON array.
|
||||||
ArrayAppend - Append a value onto a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayAppend(value interface{}, path ...string) error {
|
func (g *Container) ArrayAppend(value interface{}, path ...string) error {
|
||||||
array, ok := g.Search(path...).Data().([]interface{})
|
array, ok := g.Search(path...).Data().([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -374,16 +318,12 @@ func (g *Container) ArrayAppend(value interface{}, path ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path.
|
||||||
ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayAppendP(value interface{}, path string) error {
|
func (g *Container) ArrayAppendP(value interface{}, path string) error {
|
||||||
return g.ArrayAppend(value, strings.Split(path, ".")...)
|
return g.ArrayAppend(value, strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayRemove - Remove an element from a JSON array.
|
||||||
ArrayRemove - Remove an element from a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayRemove(index int, path ...string) error {
|
func (g *Container) ArrayRemove(index int, path ...string) error {
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
return ErrOutOfBounds
|
return ErrOutOfBounds
|
||||||
|
@ -401,16 +341,12 @@ func (g *Container) ArrayRemove(index int, path ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path.
|
||||||
ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayRemoveP(index int, path string) error {
|
func (g *Container) ArrayRemoveP(index int, path string) error {
|
||||||
return g.ArrayRemove(index, strings.Split(path, ".")...)
|
return g.ArrayRemove(index, strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayElement - Access an element from a JSON array.
|
||||||
ArrayElement - Access an element from a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayElement(index int, path ...string) (*Container, error) {
|
func (g *Container) ArrayElement(index int, path ...string) (*Container, error) {
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
return &Container{nil}, ErrOutOfBounds
|
return &Container{nil}, ErrOutOfBounds
|
||||||
|
@ -425,16 +361,12 @@ func (g *Container) ArrayElement(index int, path ...string) (*Container, error)
|
||||||
return &Container{nil}, ErrOutOfBounds
|
return &Container{nil}, ErrOutOfBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayElementP - Access an element from a JSON array using a dot notation JSON path.
|
||||||
ArrayElementP - Access an element from a JSON array using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayElementP(index int, path string) (*Container, error) {
|
func (g *Container) ArrayElementP(index int, path string) (*Container, error) {
|
||||||
return g.ArrayElement(index, strings.Split(path, ".")...)
|
return g.ArrayElement(index, strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayCount - Count the number of elements in a JSON array.
|
||||||
ArrayCount - Count the number of elements in a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayCount(path ...string) (int, error) {
|
func (g *Container) ArrayCount(path ...string) (int, error) {
|
||||||
if array, ok := g.Search(path...).Data().([]interface{}); ok {
|
if array, ok := g.Search(path...).Data().([]interface{}); ok {
|
||||||
return len(array), nil
|
return len(array), nil
|
||||||
|
@ -442,19 +374,14 @@ func (g *Container) ArrayCount(path ...string) (int, error) {
|
||||||
return 0, ErrNotArray
|
return 0, ErrNotArray
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path.
|
||||||
ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path.
|
|
||||||
*/
|
|
||||||
func (g *Container) ArrayCountP(path string) (int, error) {
|
func (g *Container) ArrayCountP(path string) (int, error) {
|
||||||
return g.ArrayCount(strings.Split(path, ".")...)
|
return g.ArrayCount(strings.Split(path, ".")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
// Bytes - Converts the contained object back to a JSON []byte blob.
|
||||||
Bytes - Converts the contained object back to a JSON []byte blob.
|
|
||||||
*/
|
|
||||||
func (g *Container) Bytes() []byte {
|
func (g *Container) Bytes() []byte {
|
||||||
if g.object != nil {
|
if g.object != nil {
|
||||||
if bytes, err := json.Marshal(g.object); err == nil {
|
if bytes, err := json.Marshal(g.object); err == nil {
|
||||||
|
@ -464,9 +391,7 @@ func (g *Container) Bytes() []byte {
|
||||||
return []byte("{}")
|
return []byte("{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// BytesIndent - Converts the contained object to a JSON []byte blob formatted with prefix, indent.
|
||||||
BytesIndent - Converts the contained object back to a JSON []byte blob formatted with prefix and indent.
|
|
||||||
*/
|
|
||||||
func (g *Container) BytesIndent(prefix string, indent string) []byte {
|
func (g *Container) BytesIndent(prefix string, indent string) []byte {
|
||||||
if g.object != nil {
|
if g.object != nil {
|
||||||
if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil {
|
if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil {
|
||||||
|
@ -476,37 +401,27 @@ func (g *Container) BytesIndent(prefix string, indent string) []byte {
|
||||||
return []byte("{}")
|
return []byte("{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// String - Converts the contained object to a JSON formatted string.
|
||||||
String - Converts the contained object back to a JSON formatted string.
|
|
||||||
*/
|
|
||||||
func (g *Container) String() string {
|
func (g *Container) String() string {
|
||||||
return string(g.Bytes())
|
return string(g.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// StringIndent - Converts the contained object back to a JSON formatted string with prefix, indent.
|
||||||
StringIndent - Converts the contained object back to a JSON formatted string with prefix and indent.
|
|
||||||
*/
|
|
||||||
func (g *Container) StringIndent(prefix string, indent string) string {
|
func (g *Container) StringIndent(prefix string, indent string) string {
|
||||||
return string(g.BytesIndent(prefix, indent))
|
return string(g.BytesIndent(prefix, indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// New - Create a new gabs JSON object.
|
||||||
New - Create a new gabs JSON object.
|
|
||||||
*/
|
|
||||||
func New() *Container {
|
func New() *Container {
|
||||||
return &Container{map[string]interface{}{}}
|
return &Container{map[string]interface{}{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object.
|
||||||
Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object.
|
|
||||||
*/
|
|
||||||
func Consume(root interface{}) (*Container, error) {
|
func Consume(root interface{}) (*Container, error) {
|
||||||
return &Container{root}, nil
|
return &Container{root}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ParseJSON - Convert a string into a representation of the parsed JSON.
|
||||||
ParseJSON - Convert a string into a representation of the parsed JSON.
|
|
||||||
*/
|
|
||||||
func ParseJSON(sample []byte) (*Container, error) {
|
func ParseJSON(sample []byte) (*Container, error) {
|
||||||
var gabs Container
|
var gabs Container
|
||||||
|
|
||||||
|
@ -517,9 +432,7 @@ func ParseJSON(sample []byte) (*Container, error) {
|
||||||
return &gabs, nil
|
return &gabs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON.
|
||||||
ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON.
|
|
||||||
*/
|
|
||||||
func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
|
func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
|
||||||
var gabs Container
|
var gabs Container
|
||||||
|
|
||||||
|
@ -530,9 +443,7 @@ func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
|
||||||
return &gabs, nil
|
return &gabs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ParseJSONFile - Read a file and convert into a representation of the parsed JSON.
|
||||||
ParseJSONFile - Read a file and convert into a representation of the parsed JSON.
|
|
||||||
*/
|
|
||||||
func ParseJSONFile(path string) (*Container, error) {
|
func ParseJSONFile(path string) (*Container, error) {
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
cBytes, err := ioutil.ReadFile(path)
|
cBytes, err := ioutil.ReadFile(path)
|
||||||
|
@ -550,9 +461,7 @@ func ParseJSONFile(path string) (*Container, error) {
|
||||||
return nil, ErrInvalidPath
|
return nil, ErrInvalidPath
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON.
|
||||||
ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON.
|
|
||||||
*/
|
|
||||||
func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
|
func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
|
||||||
var gabs Container
|
var gabs Container
|
||||||
jsonDecoder := json.NewDecoder(buffer)
|
jsonDecoder := json.NewDecoder(buffer)
|
||||||
|
@ -563,83 +472,4 @@ func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
|
||||||
return &gabs, nil
|
return &gabs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
*/
|
|
||||||
|
|
||||||
// DEPRECATED METHODS
|
|
||||||
|
|
||||||
/*
|
|
||||||
Push - DEPRECATED: Push a value onto a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) Push(target string, value interface{}) error {
|
|
||||||
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
|
||||||
arrayTarget := mmap[target]
|
|
||||||
if array, ok := arrayTarget.([]interface{}); ok {
|
|
||||||
mmap[target] = append(array, value)
|
|
||||||
} else {
|
|
||||||
return ErrNotArray
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ErrNotObj
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
RemoveElement - DEPRECATED: Remove a value from a JSON array.
|
|
||||||
*/
|
|
||||||
func (g *Container) RemoveElement(target string, index int) error {
|
|
||||||
if index < 0 {
|
|
||||||
return ErrOutOfBounds
|
|
||||||
}
|
|
||||||
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
|
||||||
arrayTarget := mmap[target]
|
|
||||||
if array, ok := arrayTarget.([]interface{}); ok {
|
|
||||||
if index < len(array) {
|
|
||||||
mmap[target] = append(array[:index], array[index+1:]...)
|
|
||||||
} else {
|
|
||||||
return ErrOutOfBounds
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ErrNotArray
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ErrNotObj
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetElement - DEPRECATED: Get the desired element from a JSON array
|
|
||||||
*/
|
|
||||||
func (g *Container) GetElement(target string, index int) *Container {
|
|
||||||
if index < 0 {
|
|
||||||
return &Container{nil}
|
|
||||||
}
|
|
||||||
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
|
||||||
arrayTarget := mmap[target]
|
|
||||||
if array, ok := arrayTarget.([]interface{}); ok {
|
|
||||||
if index < len(array) {
|
|
||||||
return &Container{array[index]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Container{nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
CountElements - DEPRECATED: Count the elements of a JSON array, returns -1 if the target is not an
|
|
||||||
array
|
|
||||||
*/
|
|
||||||
func (g *Container) CountElements(target string) int {
|
|
||||||
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
|
||||||
arrayTarget := mmap[target]
|
|
||||||
if array, ok := arrayTarget.([]interface{}); ok {
|
|
||||||
return len(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
|
@ -28,39 +28,42 @@ Examples
|
||||||
|
|
||||||
Here is an example of using the package:
|
Here is an example of using the package:
|
||||||
|
|
||||||
func SlowMethod() {
|
```go
|
||||||
// Profiling the runtime of a method
|
func SlowMethod() {
|
||||||
defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now())
|
// Profiling the runtime of a method
|
||||||
}
|
defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
// Configure a statsite sink as the global metrics sink
|
// Configure a statsite sink as the global metrics sink
|
||||||
sink, _ := metrics.NewStatsiteSink("statsite:8125")
|
sink, _ := metrics.NewStatsiteSink("statsite:8125")
|
||||||
metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink)
|
metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink)
|
||||||
|
|
||||||
// Emit a Key/Value pair
|
|
||||||
metrics.EmitKey([]string{"questions", "meaning of life"}, 42)
|
|
||||||
|
|
||||||
|
// Emit a Key/Value pair
|
||||||
|
metrics.EmitKey([]string{"questions", "meaning of life"}, 42)
|
||||||
|
```
|
||||||
|
|
||||||
Here is an example of setting up an signal handler:
|
Here is an example of setting up an signal handler:
|
||||||
|
|
||||||
// Setup the inmem sink and signal handler
|
```go
|
||||||
inm := metrics.NewInmemSink(10*time.Second, time.Minute)
|
// Setup the inmem sink and signal handler
|
||||||
sig := metrics.DefaultInmemSignal(inm)
|
inm := metrics.NewInmemSink(10*time.Second, time.Minute)
|
||||||
metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm)
|
sig := metrics.DefaultInmemSignal(inm)
|
||||||
|
metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm)
|
||||||
|
|
||||||
// Run some code
|
// Run some code
|
||||||
inm.SetGauge([]string{"foo"}, 42)
|
inm.SetGauge([]string{"foo"}, 42)
|
||||||
inm.EmitKey([]string{"bar"}, 30)
|
inm.EmitKey([]string{"bar"}, 30)
|
||||||
|
|
||||||
inm.IncrCounter([]string{"baz"}, 42)
|
inm.IncrCounter([]string{"baz"}, 42)
|
||||||
inm.IncrCounter([]string{"baz"}, 1)
|
inm.IncrCounter([]string{"baz"}, 1)
|
||||||
inm.IncrCounter([]string{"baz"}, 80)
|
inm.IncrCounter([]string{"baz"}, 80)
|
||||||
|
|
||||||
inm.AddSample([]string{"method", "wow"}, 42)
|
inm.AddSample([]string{"method", "wow"}, 42)
|
||||||
inm.AddSample([]string{"method", "wow"}, 100)
|
inm.AddSample([]string{"method", "wow"}, 100)
|
||||||
inm.AddSample([]string{"method", "wow"}, 22)
|
inm.AddSample([]string{"method", "wow"}, 22)
|
||||||
|
|
||||||
....
|
....
|
||||||
|
```
|
||||||
|
|
||||||
When a signal comes in, output like the following will be dumped to stderr:
|
When a signal comes in, output like the following will be dumped to stderr:
|
||||||
|
|
||||||
|
|
|
@ -182,6 +182,19 @@ type Config struct {
|
||||||
// the delay of a request see the aws/client.DefaultRetryer and
|
// the delay of a request see the aws/client.DefaultRetryer and
|
||||||
// aws/request.Retryer.
|
// aws/request.Retryer.
|
||||||
SleepDelay func(time.Duration)
|
SleepDelay func(time.Duration)
|
||||||
|
|
||||||
|
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
|
||||||
|
// Will default to false. This would only be used for empty directory names in s3 requests.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// sess, err := session.NewSession(&aws.Config{DisableRestProtocolURICleaning: aws.Bool(true))
|
||||||
|
//
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
// out, err := svc.GetObject(&s3.GetObjectInput {
|
||||||
|
// Bucket: aws.String("bucketname"),
|
||||||
|
// Key: aws.String("//foo//bar//moo"),
|
||||||
|
// })
|
||||||
|
DisableRestProtocolURICleaning *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig returns a new Config pointer that can be chained with builder
|
// NewConfig returns a new Config pointer that can be chained with builder
|
||||||
|
@ -403,6 +416,10 @@ func mergeInConfig(dst *Config, other *Config) {
|
||||||
if other.SleepDelay != nil {
|
if other.SleepDelay != nil {
|
||||||
dst.SleepDelay = other.SleepDelay
|
dst.SleepDelay = other.SleepDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if other.DisableRestProtocolURICleaning != nil {
|
||||||
|
dst.DisableRestProtocolURICleaning = other.DisableRestProtocolURICleaning
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy will return a shallow copy of the Config object. If any additional
|
// Copy will return a shallow copy of the Config object. If any additional
|
||||||
|
|
2
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
2
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
|
@ -111,7 +111,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ec2RoleCredRespBody provides the shape for unmarshalling credential
|
// A ec2RoleCredRespBody provides the shape for unmarshaling credential
|
||||||
// request responses.
|
// request responses.
|
||||||
type ec2RoleCredRespBody struct {
|
type ec2RoleCredRespBody struct {
|
||||||
// Success State
|
// Success State
|
||||||
|
|
|
@ -133,7 +133,7 @@ func (c *EC2Metadata) Available() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// An EC2IAMInfo provides the shape for unmarshalling
|
// An EC2IAMInfo provides the shape for unmarshaling
|
||||||
// an IAM info from the metadata API
|
// an IAM info from the metadata API
|
||||||
type EC2IAMInfo struct {
|
type EC2IAMInfo struct {
|
||||||
Code string
|
Code string
|
||||||
|
@ -142,7 +142,7 @@ type EC2IAMInfo struct {
|
||||||
InstanceProfileID string
|
InstanceProfileID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// An EC2InstanceIdentityDocument provides the shape for unmarshalling
|
// An EC2InstanceIdentityDocument provides the shape for unmarshaling
|
||||||
// an instance identity document
|
// an instance identity document
|
||||||
type EC2InstanceIdentityDocument struct {
|
type EC2InstanceIdentityDocument struct {
|
||||||
DevpayProductCodes []string `json:"devpayProductCodes"`
|
DevpayProductCodes []string `json:"devpayProductCodes"`
|
||||||
|
|
|
@ -567,7 +567,11 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *signingCtx) buildCanonicalString() {
|
func (ctx *signingCtx) buildCanonicalString() {
|
||||||
ctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), "+", "%20", -1)
|
query := ctx.Query
|
||||||
|
for key := range query {
|
||||||
|
sort.Strings(query[key])
|
||||||
|
}
|
||||||
|
ctx.Request.URL.RawQuery = strings.Replace(query.Encode(), "+", "%20", -1)
|
||||||
|
|
||||||
uri := getURIPath(ctx.Request.URL)
|
uri := getURIPath(ctx.Request.URL)
|
||||||
|
|
||||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
||||||
const SDKName = "aws-sdk-go"
|
const SDKName = "aws-sdk-go"
|
||||||
|
|
||||||
// SDKVersion is the version of this SDK
|
// SDKVersion is the version of this SDK
|
||||||
const SDKVersion = "1.5.0"
|
const SDKVersion = "1.5.6"
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
)
|
)
|
||||||
|
@ -92,7 +93,7 @@ func buildLocationElements(r *request.Request, v reflect.Value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
r.HTTPRequest.URL.RawQuery = query.Encode()
|
r.HTTPRequest.URL.RawQuery = query.Encode()
|
||||||
updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path)
|
updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path, aws.BoolValue(r.Config.DisableRestProtocolURICleaning))
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildBody(r *request.Request, v reflect.Value) {
|
func buildBody(r *request.Request, v reflect.Value) {
|
||||||
|
@ -193,13 +194,15 @@ func buildQueryString(query url.Values, v reflect.Value, name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePath(url *url.URL, urlPath string) {
|
func updatePath(url *url.URL, urlPath string, disableRestProtocolURICleaning bool) {
|
||||||
scheme, query := url.Scheme, url.RawQuery
|
scheme, query := url.Scheme, url.RawQuery
|
||||||
|
|
||||||
hasSlash := strings.HasSuffix(urlPath, "/")
|
hasSlash := strings.HasSuffix(urlPath, "/")
|
||||||
|
|
||||||
// clean up path
|
// clean up path
|
||||||
urlPath = path.Clean(urlPath)
|
if !disableRestProtocolURICleaning {
|
||||||
|
urlPath = path.Clean(urlPath)
|
||||||
|
}
|
||||||
if hasSlash && !strings.HasSuffix(urlPath, "/") {
|
if hasSlash && !strings.HasSuffix(urlPath, "/") {
|
||||||
urlPath += "/"
|
urlPath += "/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ Lucas Liu <extrafliu at gmail.com>
|
||||||
Luke Scott <luke at webconnex.com>
|
Luke Scott <luke at webconnex.com>
|
||||||
Michael Woolnough <michael.woolnough at gmail.com>
|
Michael Woolnough <michael.woolnough at gmail.com>
|
||||||
Nicola Peduzzi <thenikso at gmail.com>
|
Nicola Peduzzi <thenikso at gmail.com>
|
||||||
|
Olivier Mengué <dolmen at cpan.org>
|
||||||
Paul Bonser <misterpib at gmail.com>
|
Paul Bonser <misterpib at gmail.com>
|
||||||
Runrioter Wung <runrioter at gmail.com>
|
Runrioter Wung <runrioter at gmail.com>
|
||||||
Soroush Pour <me at soroushjp.com>
|
Soroush Pour <me at soroushjp.com>
|
||||||
|
|
|
@ -315,13 +315,21 @@ I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms
|
||||||
|
|
||||||
##### System Variables
|
##### System Variables
|
||||||
|
|
||||||
All other parameters are interpreted as system variables:
|
Any other parameters are interpreted as system variables:
|
||||||
* `autocommit`: `"SET autocommit=<value>"`
|
* `<boolean_var>=<value>`: `SET <boolean_var>=<value>`
|
||||||
* [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone=<value>"`
|
* `<enum_var>=<value>`: `SET <enum_var>=<value>`
|
||||||
* [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"`
|
* `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
|
||||||
* `param`: `"SET <param>=<value>"`
|
|
||||||
|
Rules:
|
||||||
|
* The values for string variables must be quoted with '
|
||||||
|
* The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
|
||||||
|
(which implies values of string variables must be wrapped with `%27`)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* `autocommit=1`: `SET autocommit=1`
|
||||||
|
* [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
|
||||||
|
* [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'`
|
||||||
|
|
||||||
*The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!*
|
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
```
|
```
|
||||||
|
|
|
@ -25,9 +25,9 @@ import (
|
||||||
|
|
||||||
// Read packet to buffer 'data'
|
// Read packet to buffer 'data'
|
||||||
func (mc *mysqlConn) readPacket() ([]byte, error) {
|
func (mc *mysqlConn) readPacket() ([]byte, error) {
|
||||||
var payload []byte
|
var prevData []byte
|
||||||
for {
|
for {
|
||||||
// Read packet header
|
// read packet header
|
||||||
data, err := mc.buf.readNext(4)
|
data, err := mc.buf.readNext(4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errLog.Print(err)
|
errLog.Print(err)
|
||||||
|
@ -35,16 +35,10 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet Length [24 bit]
|
// packet length [24 bit]
|
||||||
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
|
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
|
||||||
|
|
||||||
if pktLen < 1 {
|
// check packet sync [8 bit]
|
||||||
errLog.Print(ErrMalformPkt)
|
|
||||||
mc.Close()
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check Packet Sync [8 bit]
|
|
||||||
if data[3] != mc.sequence {
|
if data[3] != mc.sequence {
|
||||||
if data[3] > mc.sequence {
|
if data[3] > mc.sequence {
|
||||||
return nil, ErrPktSyncMul
|
return nil, ErrPktSyncMul
|
||||||
|
@ -53,7 +47,20 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
||||||
}
|
}
|
||||||
mc.sequence++
|
mc.sequence++
|
||||||
|
|
||||||
// Read packet body [pktLen bytes]
|
// packets with length 0 terminate a previous packet which is a
|
||||||
|
// multiple of (2^24)−1 bytes long
|
||||||
|
if pktLen == 0 {
|
||||||
|
// there was no previous packet
|
||||||
|
if prevData == nil {
|
||||||
|
errLog.Print(ErrMalformPkt)
|
||||||
|
mc.Close()
|
||||||
|
return nil, driver.ErrBadConn
|
||||||
|
}
|
||||||
|
|
||||||
|
return prevData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// read packet body [pktLen bytes]
|
||||||
data, err = mc.buf.readNext(pktLen)
|
data, err = mc.buf.readNext(pktLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errLog.Print(err)
|
errLog.Print(err)
|
||||||
|
@ -61,18 +68,17 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
|
|
||||||
isLastPacket := (pktLen < maxPacketSize)
|
// return data if this was the last packet
|
||||||
|
if pktLen < maxPacketSize {
|
||||||
|
// zero allocations for non-split packets
|
||||||
|
if prevData == nil {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Zero allocations for non-splitting packets
|
return append(prevData, data...), nil
|
||||||
if isLastPacket && payload == nil {
|
|
||||||
return data, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = append(payload, data...)
|
prevData = append(prevData, data...)
|
||||||
|
|
||||||
if isLastPacket {
|
|
||||||
return payload, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,11 +706,15 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
||||||
if data[0] == iEOF && len(data) == 5 {
|
if data[0] == iEOF && len(data) == 5 {
|
||||||
// server_status [2 bytes]
|
// server_status [2 bytes]
|
||||||
rows.mc.status = readStatus(data[3:])
|
rows.mc.status = readStatus(data[3:])
|
||||||
if err := rows.mc.discardResults(); err != nil {
|
err = rows.mc.discardResults()
|
||||||
return err
|
if err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
} else {
|
||||||
|
// connection unusable
|
||||||
|
rows.mc.Close()
|
||||||
}
|
}
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
return io.EOF
|
return err
|
||||||
}
|
}
|
||||||
if data[0] == iERR {
|
if data[0] == iERR {
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
|
@ -1105,11 +1115,15 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
// EOF Packet
|
// EOF Packet
|
||||||
if data[0] == iEOF && len(data) == 5 {
|
if data[0] == iEOF && len(data) == 5 {
|
||||||
rows.mc.status = readStatus(data[3:])
|
rows.mc.status = readStatus(data[3:])
|
||||||
if err := rows.mc.discardResults(); err != nil {
|
err = rows.mc.discardResults()
|
||||||
return err
|
if err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
} else {
|
||||||
|
// connection unusable
|
||||||
|
rows.mc.Close()
|
||||||
}
|
}
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
return io.EOF
|
return err
|
||||||
}
|
}
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,10 @@ type mysqlStmt struct {
|
||||||
|
|
||||||
func (stmt *mysqlStmt) Close() error {
|
func (stmt *mysqlStmt) Close() error {
|
||||||
if stmt.mc == nil || stmt.mc.netConn == nil {
|
if stmt.mc == nil || stmt.mc.netConn == nil {
|
||||||
errLog.Print(ErrInvalidConn)
|
// driver.Stmt.Close can be called more than once, thus this function
|
||||||
|
// has to be idempotent.
|
||||||
|
// See also Issue #450 and golang/go#16019.
|
||||||
|
//errLog.Print(ErrInvalidConn)
|
||||||
return driver.ErrBadConn
|
return driver.ErrBadConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,3 +82,4 @@ Robert Nix <robert@nicerobot.org>
|
||||||
Nathan Youngman <git@nathany.com>
|
Nathan Youngman <git@nathany.com>
|
||||||
Charles Law <charles.law@gmail.com>; <claw@conduce.com>
|
Charles Law <charles.law@gmail.com>; <claw@conduce.com>
|
||||||
Nathan Davies <nathanjamesdavies@gmail.com>
|
Nathan Davies <nathanjamesdavies@gmail.com>
|
||||||
|
Bo Blanton <bo.blanton@gmail.com>
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package gocql
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
// AddressTranslator provides a way to translate node addresses (and ports) that are
|
||||||
|
// discovered or received as a node event. This can be useful in an ec2 environment,
|
||||||
|
// for instance, to translate public IPs to private IPs.
|
||||||
|
type AddressTranslator interface {
|
||||||
|
// Translate will translate the provided address and/or port to another
|
||||||
|
// address and/or port. If no translation is possible, Translate will return the
|
||||||
|
// address and port provided to it.
|
||||||
|
Translate(addr net.IP, port int) (net.IP, int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressTranslatorFunc func(addr net.IP, port int) (net.IP, int)
|
||||||
|
|
||||||
|
func (fn AddressTranslatorFunc) Translate(addr net.IP, port int) (net.IP, int) {
|
||||||
|
return fn(addr, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentityTranslator will do nothing but return what it was provided. It is essentially a no-op.
|
||||||
|
func IdentityTranslator() AddressTranslator {
|
||||||
|
return AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) {
|
||||||
|
return addr, port
|
||||||
|
})
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ package gocql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -75,6 +77,10 @@ type ClusterConfig struct {
|
||||||
// via Discovery
|
// via Discovery
|
||||||
HostFilter HostFilter
|
HostFilter HostFilter
|
||||||
|
|
||||||
|
// AddressTranslator will translate addresses found on peer discovery and/or
|
||||||
|
// node change events.
|
||||||
|
AddressTranslator AddressTranslator
|
||||||
|
|
||||||
// If IgnorePeerAddr is true and the address in system.peers does not match
|
// If IgnorePeerAddr is true and the address in system.peers does not match
|
||||||
// the supplied host by either initial hosts or discovered via events then the
|
// the supplied host by either initial hosts or discovered via events then the
|
||||||
// host will be replaced with the supplied address.
|
// host will be replaced with the supplied address.
|
||||||
|
@ -146,6 +152,21 @@ func (cfg *ClusterConfig) CreateSession() (*Session, error) {
|
||||||
return NewSession(*cfg)
|
return NewSession(*cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// translateAddressPort is a helper method that will use the given AddressTranslator
|
||||||
|
// if defined, to translate the given address and port into a possibly new address
|
||||||
|
// and port, If no AddressTranslator or if an error occurs, the given address and
|
||||||
|
// port will be returned.
|
||||||
|
func (cfg *ClusterConfig) translateAddressPort(addr net.IP, port int) (net.IP, int) {
|
||||||
|
if cfg.AddressTranslator == nil || len(addr) == 0 {
|
||||||
|
return addr, port
|
||||||
|
}
|
||||||
|
newAddr, newPort := cfg.AddressTranslator.Translate(addr, port)
|
||||||
|
if gocqlDebug {
|
||||||
|
log.Printf("gocql: translating address '%v:%d' to '%v:%d'", addr, port, newAddr, newPort)
|
||||||
|
}
|
||||||
|
return newAddr, newPort
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoHosts = errors.New("no hosts provided")
|
ErrNoHosts = errors.New("no hosts provided")
|
||||||
ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
|
ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
|
||||||
|
|
|
@ -172,7 +172,9 @@ func Connect(host *HostInfo, cfg *ConnConfig, errorHandler ConnErrorHandler, ses
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(zariel): handle ipv6 zone
|
// TODO(zariel): handle ipv6 zone
|
||||||
addr := (&net.TCPAddr{IP: host.Peer(), Port: host.Port()}).String()
|
translatedPeer, translatedPort := session.cfg.translateAddressPort(host.Peer(), host.Port())
|
||||||
|
addr := (&net.TCPAddr{IP: translatedPeer, Port: translatedPort}).String()
|
||||||
|
//addr := (&net.TCPAddr{IP: host.Peer(), Port: host.Port()}).String()
|
||||||
|
|
||||||
if cfg.tlsConfig != nil {
|
if cfg.tlsConfig != nil {
|
||||||
// the TLS config is safe to be reused by connections but it must not
|
// the TLS config is safe to be reused by connections but it must not
|
||||||
|
|
|
@ -318,8 +318,8 @@ func (c *controlConn) reconnect(refreshring bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should have our own roundrobbin for hosts so that we can try each
|
// TODO: should have our own round-robin for hosts so that we can try each
|
||||||
// in succession and guantee that we get a different host each time.
|
// in succession and guarantee that we get a different host each time.
|
||||||
if newConn == nil {
|
if newConn == nil {
|
||||||
host := c.session.ring.rrHost()
|
host := c.session.ring.rrHost()
|
||||||
if host == nil {
|
if host == nil {
|
||||||
|
|
|
@ -205,7 +205,6 @@ func (s *Session) handleNewNode(ip net.IP, port int, waitForBinary bool) {
|
||||||
s.pool.addHost(hostInfo)
|
s.pool.addHost(hostInfo)
|
||||||
s.policy.AddHost(hostInfo)
|
s.policy.AddHost(hostInfo)
|
||||||
hostInfo.setState(NodeUp)
|
hostInfo.setState(NodeUp)
|
||||||
|
|
||||||
if s.control != nil && !s.cfg.IgnorePeerAddr {
|
if s.control != nil && !s.cfg.IgnorePeerAddr {
|
||||||
s.hostSource.refreshRing()
|
s.hostSource.refreshRing()
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ function run_tests() {
|
||||||
|
|
||||||
local args="-gocql.timeout=60s -runssl -proto=$proto -rf=3 -clusterSize=$clusterSize -autowait=2000ms -compressor=snappy -gocql.cversion=$version -cluster=$(ccm liveset) ./..."
|
local args="-gocql.timeout=60s -runssl -proto=$proto -rf=3 -clusterSize=$clusterSize -autowait=2000ms -compressor=snappy -gocql.cversion=$version -cluster=$(ccm liveset) ./..."
|
||||||
|
|
||||||
go test -v -tags unit
|
go test -v -tags unit
|
||||||
|
|
||||||
if [ "$auth" = true ]
|
if [ "$auth" = true ]
|
||||||
then
|
then
|
||||||
|
|
|
@ -204,14 +204,21 @@ func (t *tokenRing) GetHostForToken(token token) *HostInfo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l := len(t.tokens)
|
||||||
|
// no host tokens, no available hosts
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// find the primary replica
|
// find the primary replica
|
||||||
ringIndex := sort.Search(
|
ringIndex := sort.Search(
|
||||||
len(t.tokens),
|
l,
|
||||||
func(i int) bool {
|
func(i int) bool {
|
||||||
return !t.tokens[i].Less(token)
|
return !t.tokens[i].Less(token)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if ringIndex == len(t.tokens) {
|
|
||||||
|
if ringIndex == l {
|
||||||
// wrap around to the first in the ring
|
// wrap around to the first in the ring
|
||||||
ringIndex = 0
|
ringIndex = 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,7 @@ type ExtensionDesc struct {
|
||||||
Field int32 // field number
|
Field int32 // field number
|
||||||
Name string // fully-qualified name of extension, for text formatting
|
Name string // fully-qualified name of extension, for text formatting
|
||||||
Tag string // protobuf tag style
|
Tag string // protobuf tag style
|
||||||
|
Filename string // name of the file in which the extension is defined
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ed *ExtensionDesc) repeated() bool {
|
func (ed *ExtensionDesc) repeated() bool {
|
||||||
|
|
|
@ -592,7 +592,11 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
||||||
props = oop.Prop
|
props = oop.Prop
|
||||||
nv := reflect.New(oop.Type.Elem())
|
nv := reflect.New(oop.Type.Elem())
|
||||||
dst = nv.Elem().Field(0)
|
dst = nv.Elem().Field(0)
|
||||||
sv.Field(oop.Field).Set(nv)
|
field := sv.Field(oop.Field)
|
||||||
|
if !field.IsNil() {
|
||||||
|
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
|
||||||
|
}
|
||||||
|
field.Set(nv)
|
||||||
}
|
}
|
||||||
if !dst.IsValid() {
|
if !dst.IsValid() {
|
||||||
return p.errorf("unknown field name %q in %v", name, st)
|
return p.errorf("unknown field name %q in %v", name, st)
|
||||||
|
|
|
@ -55,10 +55,14 @@ func (e *Event) Payload() (payload interface{}) {
|
||||||
payload = &IssueCommentEvent{}
|
payload = &IssueCommentEvent{}
|
||||||
case "IssuesEvent":
|
case "IssuesEvent":
|
||||||
payload = &IssuesEvent{}
|
payload = &IssuesEvent{}
|
||||||
|
case "LabelEvent":
|
||||||
|
payload = &LabelEvent{}
|
||||||
case "MemberEvent":
|
case "MemberEvent":
|
||||||
payload = &MemberEvent{}
|
payload = &MemberEvent{}
|
||||||
case "MembershipEvent":
|
case "MembershipEvent":
|
||||||
payload = &MembershipEvent{}
|
payload = &MembershipEvent{}
|
||||||
|
case "MilestoneEvent":
|
||||||
|
payload = &MilestoneEvent{}
|
||||||
case "PageBuildEvent":
|
case "PageBuildEvent":
|
||||||
payload = &PageBuildEvent{}
|
payload = &PageBuildEvent{}
|
||||||
case "PublicEvent":
|
case "PublicEvent":
|
||||||
|
|
|
@ -346,9 +346,6 @@ func (s *AuthorizationsService) ListGrants() ([]*Grant, *Response, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
|
|
||||||
|
|
||||||
grants := []*Grant{}
|
grants := []*Grant{}
|
||||||
resp, err := s.client.Do(req, &grants)
|
resp, err := s.client.Do(req, &grants)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -368,9 +365,6 @@ func (s *AuthorizationsService) GetGrant(id int) (*Grant, *Response, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
|
|
||||||
|
|
||||||
grant := new(Grant)
|
grant := new(Grant)
|
||||||
resp, err := s.client.Do(req, grant)
|
resp, err := s.client.Do(req, grant)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -392,9 +386,6 @@ func (s *AuthorizationsService) DeleteGrant(id int) (*Response, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
|
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
return s.client.Do(req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -209,6 +209,22 @@ type IssuesEvent struct {
|
||||||
Sender *User `json:"sender,omitempty"`
|
Sender *User `json:"sender,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelEvent is triggered when a repository's label is created, edited, or deleted.
|
||||||
|
// The Webhook event name is "label"
|
||||||
|
//
|
||||||
|
// GitHub docs: https://developer.github.com/v3/activity/events/types/#labelevent
|
||||||
|
type LabelEvent struct {
|
||||||
|
// Action is the action that was performed. Possible values are:
|
||||||
|
// "created", "edited", "deleted"
|
||||||
|
Action *string `json:"action,omitempty"`
|
||||||
|
Label *Label `json:"label,omitempty"`
|
||||||
|
|
||||||
|
// The following fields are only populated by Webhook events.
|
||||||
|
Changes *EditChange `json:"changes,omitempty"`
|
||||||
|
Repo *Repository `json:"repository,omitempty"`
|
||||||
|
Org *Organization `json:"organization,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// MemberEvent is triggered when a user is added as a collaborator to a repository.
|
// MemberEvent is triggered when a user is added as a collaborator to a repository.
|
||||||
// The Webhook event name is "member".
|
// The Webhook event name is "member".
|
||||||
//
|
//
|
||||||
|
@ -243,6 +259,23 @@ type MembershipEvent struct {
|
||||||
Sender *User `json:"sender,omitempty"`
|
Sender *User `json:"sender,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted.
|
||||||
|
// The Webhook event name is "milestone".
|
||||||
|
//
|
||||||
|
// Github docs: https://developer.github.com/v3/activity/events/types/#milestoneevent
|
||||||
|
type MilestoneEvent struct {
|
||||||
|
// Action is the action that was performed. Possible values are:
|
||||||
|
// "created", "closed", "opened", "edited", "deleted"
|
||||||
|
Action *string `json:"action,omitempty"`
|
||||||
|
Milestone *Milestone `json:"milestone,omitempty"`
|
||||||
|
|
||||||
|
// The following fields are only populated by Webhook events.
|
||||||
|
Changes *EditChange `json:"changes,omitempty"`
|
||||||
|
Repo *Repository `json:"repository,omitempty"`
|
||||||
|
Sender *User `json:"sender,omitempty"`
|
||||||
|
Org *Organization `json:"organization,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether
|
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether
|
||||||
// successful or not.
|
// successful or not.
|
||||||
// The Webhook event name is "page_build".
|
// The Webhook event name is "page_build".
|
||||||
|
|
|
@ -68,6 +68,7 @@ const (
|
||||||
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
|
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
|
||||||
|
|
||||||
// https://developer.github.com/changes/2016-04-01-squash-api-preview/
|
// https://developer.github.com/changes/2016-04-01-squash-api-preview/
|
||||||
|
// https://developer.github.com/changes/2016-09-26-pull-request-merge-api-update/
|
||||||
mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json"
|
mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json"
|
||||||
|
|
||||||
// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/
|
// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/
|
||||||
|
@ -79,9 +80,6 @@ const (
|
||||||
// https://developer.github.com/changes/2016-06-14-repository-invitations/
|
// https://developer.github.com/changes/2016-06-14-repository-invitations/
|
||||||
mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json"
|
mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json"
|
||||||
|
|
||||||
// https://developer.github.com/changes/2016-04-21-oauth-authorizations-grants-api-preview/
|
|
||||||
mediaTypeOAuthGrantAuthorizationsPreview = "application/vnd.github.damage-preview+json"
|
|
||||||
|
|
||||||
// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
|
// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
|
||||||
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
|
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
|
||||||
|
|
||||||
|
@ -126,6 +124,7 @@ type Client struct {
|
||||||
Integrations *IntegrationsService
|
Integrations *IntegrationsService
|
||||||
Issues *IssuesService
|
Issues *IssuesService
|
||||||
Organizations *OrganizationsService
|
Organizations *OrganizationsService
|
||||||
|
Projects *ProjectsService
|
||||||
PullRequests *PullRequestsService
|
PullRequests *PullRequestsService
|
||||||
Repositories *RepositoriesService
|
Repositories *RepositoriesService
|
||||||
Search *SearchService
|
Search *SearchService
|
||||||
|
@ -199,6 +198,7 @@ func NewClient(httpClient *http.Client) *Client {
|
||||||
c.Licenses = (*LicensesService)(&c.common)
|
c.Licenses = (*LicensesService)(&c.common)
|
||||||
c.Migrations = (*MigrationService)(&c.common)
|
c.Migrations = (*MigrationService)(&c.common)
|
||||||
c.Organizations = (*OrganizationsService)(&c.common)
|
c.Organizations = (*OrganizationsService)(&c.common)
|
||||||
|
c.Projects = (*ProjectsService)(&c.common)
|
||||||
c.PullRequests = (*PullRequestsService)(&c.common)
|
c.PullRequests = (*PullRequestsService)(&c.common)
|
||||||
c.Reactions = (*ReactionsService)(&c.common)
|
c.Reactions = (*ReactionsService)(&c.common)
|
||||||
c.Repositories = (*RepositoriesService)(&c.common)
|
c.Repositories = (*RepositoriesService)(&c.common)
|
||||||
|
|
|
@ -10,23 +10,44 @@ import "fmt"
|
||||||
// LicensesService handles communication with the license related
|
// LicensesService handles communication with the license related
|
||||||
// methods of the GitHub API.
|
// methods of the GitHub API.
|
||||||
//
|
//
|
||||||
// GitHub API docs: http://developer.github.com/v3/pulls/
|
// GitHub API docs: https://developer.github.com/v3/licenses/
|
||||||
type LicensesService service
|
type LicensesService service
|
||||||
|
|
||||||
|
// RepositoryLicense represents the license for a repository.
|
||||||
|
type RepositoryLicense struct {
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Path *string `json:"path,omitempty"`
|
||||||
|
|
||||||
|
SHA *string `json:"sha,omitempty"`
|
||||||
|
Size *int `json:"size,omitempty"`
|
||||||
|
URL *string `json:"url,omitempty"`
|
||||||
|
HTMLURL *string `json:"html_url,omitempty"`
|
||||||
|
GitURL *string `json:"git_url,omitempty"`
|
||||||
|
DownloadURL *string `json:"download_url,omitempty"`
|
||||||
|
Type *string `json:"type,omitempty"`
|
||||||
|
Content *string `json:"content,omitempty"`
|
||||||
|
Encoding *string `json:"encoding,omitempty"`
|
||||||
|
License *License `json:"license,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l RepositoryLicense) String() string {
|
||||||
|
return Stringify(l)
|
||||||
|
}
|
||||||
|
|
||||||
// License represents an open source license.
|
// License represents an open source license.
|
||||||
type License struct {
|
type License struct {
|
||||||
Key *string `json:"key,omitempty"`
|
Key *string `json:"key,omitempty"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
URL *string `json:"url,omitempty"`
|
URL *string `json:"url,omitempty"`
|
||||||
|
|
||||||
|
SPDXID *string `json:"spdx_id,omitempty"`
|
||||||
HTMLURL *string `json:"html_url,omitempty"`
|
HTMLURL *string `json:"html_url,omitempty"`
|
||||||
Featured *bool `json:"featured,omitempty"`
|
Featured *bool `json:"featured,omitempty"`
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
Category *string `json:"category,omitempty"`
|
|
||||||
Implementation *string `json:"implementation,omitempty"`
|
Implementation *string `json:"implementation,omitempty"`
|
||||||
Required *[]string `json:"required,omitempty"`
|
Permissions *[]string `json:"permissions,omitempty"`
|
||||||
Permitted *[]string `json:"permitted,omitempty"`
|
Conditions *[]string `json:"conditions,omitempty"`
|
||||||
Forbidden *[]string `json:"forbidden,omitempty"`
|
Limitations *[]string `json:"limitations,omitempty"`
|
||||||
Body *string `json:"body,omitempty"`
|
Body *string `json:"body,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,8 +49,10 @@ var (
|
||||||
"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
|
"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
|
||||||
"issue_comment": "IssueCommentEvent",
|
"issue_comment": "IssueCommentEvent",
|
||||||
"issues": "IssuesEvent",
|
"issues": "IssuesEvent",
|
||||||
|
"label": "LabelEvent",
|
||||||
"member": "MemberEvent",
|
"member": "MemberEvent",
|
||||||
"membership": "MembershipEvent",
|
"membership": "MembershipEvent",
|
||||||
|
"milestone": "MilestoneEvent",
|
||||||
"page_build": "PageBuildEvent",
|
"page_build": "PageBuildEvent",
|
||||||
"public": "PublicEvent",
|
"public": "PublicEvent",
|
||||||
"pull_request_review_comment": "PullRequestReviewCommentEvent",
|
"pull_request_review_comment": "PullRequestReviewCommentEvent",
|
||||||
|
|
|
@ -0,0 +1,417 @@
|
||||||
|
// Copyright 2016 The go-github AUTHORS. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package github
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ProjectsService provides access to the projects functions in the
|
||||||
|
// GitHub API.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/
|
||||||
|
type ProjectsService service
|
||||||
|
|
||||||
|
// Project represents a GitHub Project.
|
||||||
|
type Project struct {
|
||||||
|
ID *int `json:"id,omitempty"`
|
||||||
|
URL *string `json:"url,omitempty"`
|
||||||
|
OwnerURL *string `json:"owner_url,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Body *string `json:"body,omitempty"`
|
||||||
|
Number *int `json:"number,omitempty"`
|
||||||
|
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
||||||
|
|
||||||
|
// The User object that generated the project.
|
||||||
|
Creator *User `json:"creator,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Project) String() string {
|
||||||
|
return Stringify(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProject gets a GitHub Project for a repo.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project
|
||||||
|
func (s *ProjectsService) GetProject(id int) (*Project, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/%v", id)
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
project := &Project{}
|
||||||
|
resp, err := s.client.Do(req, project)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return project, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectOptions specifies the parameters to the
|
||||||
|
// RepositoriesService.CreateProject and
|
||||||
|
// ProjectsService.UpdateProject methods.
|
||||||
|
type ProjectOptions struct {
|
||||||
|
// The name of the project. (Required for creation; optional for update.)
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
// The body of the project. (Optional.)
|
||||||
|
Body string `json:"body,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProject updates a repository project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project
|
||||||
|
func (s *ProjectsService) UpdateProject(id int, opt *ProjectOptions) (*Project, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/%v", id)
|
||||||
|
req, err := s.client.NewRequest("PATCH", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
project := &Project{}
|
||||||
|
resp, err := s.client.Do(req, project)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return project, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProject deletes a GitHub Project from a repository.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project
|
||||||
|
func (s *ProjectsService) DeleteProject(id int) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/%v", id)
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectColumn represents a column of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/repos/projects/
|
||||||
|
type ProjectColumn struct {
|
||||||
|
ID *int `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
ProjectURL *string `json:"project_url,omitempty"`
|
||||||
|
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListProjectColumns lists the columns of a GitHub Project for a repo.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns
|
||||||
|
func (s *ProjectsService) ListProjectColumns(projectId int, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/%v/columns", projectId)
|
||||||
|
u, err := addOptions(u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
columns := []*ProjectColumn{}
|
||||||
|
resp, err := s.client.Do(req, &columns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectColumn gets a column of a GitHub Project for a repo.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column
|
||||||
|
func (s *ProjectsService) GetProjectColumn(id int) (*ProjectColumn, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/%v", id)
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
column := &ProjectColumn{}
|
||||||
|
resp, err := s.client.Do(req, column)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return column, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectColumnOptions specifies the parameters to the
|
||||||
|
// ProjectsService.CreateProjectColumn and
|
||||||
|
// ProjectsService.UpdateProjectColumn methods.
|
||||||
|
type ProjectColumnOptions struct {
|
||||||
|
// The name of the project column. (Required for creation and update.)
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProjectColumn creates a column for the specified (by number) project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column
|
||||||
|
func (s *ProjectsService) CreateProjectColumn(projectId int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/%v/columns", projectId)
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
column := &ProjectColumn{}
|
||||||
|
resp, err := s.client.Do(req, column)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return column, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProjectColumn updates a column of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column
|
||||||
|
func (s *ProjectsService) UpdateProjectColumn(columnID int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/%v", columnID)
|
||||||
|
req, err := s.client.NewRequest("PATCH", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
column := &ProjectColumn{}
|
||||||
|
resp, err := s.client.Do(req, column)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return column, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProjectColumn deletes a column from a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column
|
||||||
|
func (s *ProjectsService) DeleteProjectColumn(columnID int) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/%v", columnID)
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectColumnMoveOptions specifies the parameters to the
|
||||||
|
// ProjectsService.MoveProjectColumn method.
|
||||||
|
type ProjectColumnMoveOptions struct {
|
||||||
|
// Position can be one of "first", "last", or "after:<column-id>", where
|
||||||
|
// <column-id> is the ID of a column in the same project. (Required.)
|
||||||
|
Position string `json:"position"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveProjectColumn moves a column within a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column
|
||||||
|
func (s *ProjectsService) MoveProjectColumn(columnID int, opt *ProjectColumnMoveOptions) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/%v/moves", columnID)
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectCard represents a card in a column of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/repos/projects/
|
||||||
|
type ProjectCard struct {
|
||||||
|
ColumnURL *string `json:"column_url,omitempty"`
|
||||||
|
ContentURL *string `json:"content_url,omitempty"`
|
||||||
|
ID *int `json:"id,omitempty"`
|
||||||
|
Note *string `json:"note,omitempty"`
|
||||||
|
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListProjectCards lists the cards in a column of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards
|
||||||
|
func (s *ProjectsService) ListProjectCards(columnID int, opt *ListOptions) ([]*ProjectCard, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/%v/cards", columnID)
|
||||||
|
u, err := addOptions(u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
cards := []*ProjectCard{}
|
||||||
|
resp, err := s.client.Do(req, &cards)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cards, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectCard gets a card in a column of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card
|
||||||
|
func (s *ProjectsService) GetProjectCard(columnID int) (*ProjectCard, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/cards/%v", columnID)
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
card := &ProjectCard{}
|
||||||
|
resp, err := s.client.Do(req, card)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return card, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectCardOptions specifies the parameters to the
|
||||||
|
// ProjectsService.CreateProjectCard and
|
||||||
|
// ProjectsService.UpdateProjectCard methods.
|
||||||
|
type ProjectCardOptions struct {
|
||||||
|
// The note of the card. Note and ContentID are mutually exclusive.
|
||||||
|
Note string `json:"note,omitempty"`
|
||||||
|
// The ID (not Number) of the Issue or Pull Request to associate with this card.
|
||||||
|
// Note and ContentID are mutually exclusive.
|
||||||
|
ContentID int `json:"content_id,omitempty"`
|
||||||
|
// The type of content to associate with this card. Possible values are: "Issue", "PullRequest".
|
||||||
|
ContentType string `json:"content_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProjectCard creates a card in the specified column of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card
|
||||||
|
func (s *ProjectsService) CreateProjectCard(columnID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/%v/cards", columnID)
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
card := &ProjectCard{}
|
||||||
|
resp, err := s.client.Do(req, card)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return card, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProjectCard updates a card of a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card
|
||||||
|
func (s *ProjectsService) UpdateProjectCard(cardID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/cards/%v", cardID)
|
||||||
|
req, err := s.client.NewRequest("PATCH", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
card := &ProjectCard{}
|
||||||
|
resp, err := s.client.Do(req, card)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return card, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProjectCard deletes a card from a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card
|
||||||
|
func (s *ProjectsService) DeleteProjectCard(cardID int) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/cards/%v", cardID)
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectCardMoveOptions specifies the parameters to the
|
||||||
|
// ProjectsService.MoveProjectCard method.
|
||||||
|
type ProjectCardMoveOptions struct {
|
||||||
|
// Position can be one of "top", "bottom", or "after:<card-id>", where
|
||||||
|
// <card-id> is the ID of a card in the same project.
|
||||||
|
Position string `json:"position"`
|
||||||
|
// ColumnID is the ID of a column in the same project. Note that ColumnID
|
||||||
|
// is required when using Position "after:<card-id>" when that card is in
|
||||||
|
// another column; otherwise it is optional.
|
||||||
|
ColumnID int `json:"column_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveProjectCard moves a card within a GitHub Project.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card
|
||||||
|
func (s *ProjectsService) MoveProjectCard(cardID int, opt *ProjectCardMoveOptions) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("/projects/columns/cards/%v/moves", cardID)
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove custom Accept header when this API fully launches.
|
||||||
|
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
|
@ -253,12 +253,16 @@ type PullRequestMergeResult struct {
|
||||||
|
|
||||||
// PullRequestOptions lets you define how a pull request will be merged.
|
// PullRequestOptions lets you define how a pull request will be merged.
|
||||||
type PullRequestOptions struct {
|
type PullRequestOptions struct {
|
||||||
Squash bool
|
CommitTitle string
|
||||||
|
|
||||||
|
// The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge.
|
||||||
|
MergeMethod string
|
||||||
}
|
}
|
||||||
|
|
||||||
type pullRequestMergeRequest struct {
|
type pullRequestMergeRequest struct {
|
||||||
CommitMessage *string `json:"commit_message"`
|
CommitMessage *string `json:"commit_message"`
|
||||||
Squash *bool `json:"squash,omitempty"`
|
CommitTitle *string `json:"commit_title,omitempty"`
|
||||||
|
MergeMethod *string `json:"merge_method,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge a pull request (Merge Button™).
|
// Merge a pull request (Merge Button™).
|
||||||
|
@ -269,7 +273,8 @@ func (s *PullRequestsService) Merge(owner string, repo string, number int, commi
|
||||||
|
|
||||||
pullRequestBody := &pullRequestMergeRequest{CommitMessage: &commitMessage}
|
pullRequestBody := &pullRequestMergeRequest{CommitMessage: &commitMessage}
|
||||||
if options != nil {
|
if options != nil {
|
||||||
pullRequestBody.Squash = &options.Squash
|
pullRequestBody.CommitTitle = &options.CommitTitle
|
||||||
|
pullRequestBody.MergeMethod = &options.MergeMethod
|
||||||
}
|
}
|
||||||
req, err := s.client.NewRequest("PUT", u, pullRequestBody)
|
req, err := s.client.NewRequest("PUT", u, pullRequestBody)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
|
|
||||||
package github
|
package github
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// RepositoriesService handles communication with the repository related
|
// RepositoriesService handles communication with the repository related
|
||||||
// methods of the GitHub API.
|
// methods of the GitHub API.
|
||||||
|
@ -46,6 +49,9 @@ type Repository struct {
|
||||||
Source *Repository `json:"source,omitempty"`
|
Source *Repository `json:"source,omitempty"`
|
||||||
Organization *Organization `json:"organization,omitempty"`
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
Permissions *map[string]bool `json:"permissions,omitempty"`
|
Permissions *map[string]bool `json:"permissions,omitempty"`
|
||||||
|
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"`
|
||||||
|
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
|
||||||
|
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"`
|
||||||
|
|
||||||
// Only provided when using RepositoriesService.Get while in preview
|
// Only provided when using RepositoriesService.Get while in preview
|
||||||
License *License `json:"license,omitempty"`
|
License *License `json:"license,omitempty"`
|
||||||
|
@ -286,7 +292,8 @@ func (s *RepositoriesService) Get(owner, repo string) (*Repository, *Response, e
|
||||||
|
|
||||||
// TODO: remove custom Accept header when the license support fully launches
|
// TODO: remove custom Accept header when the license support fully launches
|
||||||
// https://developer.github.com/v3/licenses/#get-a-repositorys-license
|
// https://developer.github.com/v3/licenses/#get-a-repositorys-license
|
||||||
req.Header.Set("Accept", mediaTypeLicensesPreview)
|
acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeSquashPreview}
|
||||||
|
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
|
||||||
|
|
||||||
repository := new(Repository)
|
repository := new(Repository)
|
||||||
resp, err := s.client.Do(req, repository)
|
resp, err := s.client.Do(req, repository)
|
||||||
|
@ -330,6 +337,9 @@ func (s *RepositoriesService) Edit(owner, repo string, repository *Repository) (
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this preview header after API is fully vetted.
|
||||||
|
req.Header.Add("Accept", mediaTypeSquashPreview)
|
||||||
|
|
||||||
r := new(Repository)
|
r := new(Repository)
|
||||||
resp, err := s.client.Do(req, r)
|
resp, err := s.client.Do(req, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -585,18 +595,18 @@ func (s *RepositoriesService) EditBranch(owner, repo, branchName string, branch
|
||||||
// License gets the contents of a repository's license if one is detected.
|
// License gets the contents of a repository's license if one is detected.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license
|
// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license
|
||||||
func (s *RepositoriesService) License(owner, repo string) (*License, *Response, error) {
|
func (s *RepositoriesService) License(owner, repo string) (*RepositoryLicense, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/license", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/license", owner, repo)
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &Repository{}
|
r := &RepositoryLicense{}
|
||||||
resp, err := s.client.Do(req, r)
|
resp, err := s.client.Do(req, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.License, resp, err
|
return r, resp, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ type PagesBuild struct {
|
||||||
// GetPagesInfo fetches information about a GitHub Pages site.
|
// GetPagesInfo fetches information about a GitHub Pages site.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site
|
// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site
|
||||||
func (s *RepositoriesService) GetPagesInfo(owner string, repo string) (*Pages, *Response, error) {
|
func (s *RepositoriesService) GetPagesInfo(owner, repo string) (*Pages, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/pages", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/pages", owner, repo)
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,7 +58,7 @@ func (s *RepositoriesService) GetPagesInfo(owner string, repo string) (*Pages, *
|
||||||
// ListPagesBuilds lists the builds for a GitHub Pages site.
|
// ListPagesBuilds lists the builds for a GitHub Pages site.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds
|
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds
|
||||||
func (s *RepositoriesService) ListPagesBuilds(owner string, repo string) ([]*PagesBuild, *Response, error) {
|
func (s *RepositoriesService) ListPagesBuilds(owner, repo string) ([]*PagesBuild, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,7 +77,7 @@ func (s *RepositoriesService) ListPagesBuilds(owner string, repo string) ([]*Pag
|
||||||
// GetLatestPagesBuild fetches the latest build information for a GitHub pages site.
|
// GetLatestPagesBuild fetches the latest build information for a GitHub pages site.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build
|
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build
|
||||||
func (s *RepositoriesService) GetLatestPagesBuild(owner string, repo string) (*PagesBuild, *Response, error) {
|
func (s *RepositoriesService) GetLatestPagesBuild(owner, repo string) (*PagesBuild, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo)
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,10 +93,29 @@ func (s *RepositoriesService) GetLatestPagesBuild(owner string, repo string) (*P
|
||||||
return build, resp, err
|
return build, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPageBuild fetches the specific build information for a GitHub pages site.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build
|
||||||
|
func (s *RepositoriesService) GetPageBuild(owner, repo string, id int) (*PagesBuild, *Response, error) {
|
||||||
|
u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id)
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
build := new(PagesBuild)
|
||||||
|
resp, err := s.client.Do(req, build)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return build, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit.
|
// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build
|
// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build
|
||||||
func (s *RepositoriesService) RequestPageBuild(owner string, repo string) (*PagesBuild, *Response, error) {
|
func (s *RepositoriesService) RequestPageBuild(owner, repo string) (*PagesBuild, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
|
||||||
req, err := s.client.NewRequest("POST", u, nil)
|
req, err := s.client.NewRequest("POST", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -5,34 +5,11 @@
|
||||||
|
|
||||||
package github
|
package github
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Project represents a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/
|
|
||||||
type Project struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
OwnerURL *string `json:"owner_url,omitempty"`
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
Body *string `json:"body,omitempty"`
|
|
||||||
Number *int `json:"number,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
|
|
||||||
// The User object that generated the project.
|
|
||||||
Creator *User `json:"creator,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Project) String() string {
|
|
||||||
return Stringify(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListProjects lists the projects for a repo.
|
// ListProjects lists the projects for a repo.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#list-projects
|
// GitHub API docs: https://developer.github.com/v3/projects/#list-repository-projects
|
||||||
func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions) ([]*Project, *Response, error) {
|
func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions) ([]*Project, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
|
||||||
u, err := addOptions(u, opt)
|
u, err := addOptions(u, opt)
|
||||||
|
@ -57,44 +34,12 @@ func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions)
|
||||||
return projects, resp, err
|
return projects, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProject gets a GitHub Project for a repo.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#get-a-project
|
|
||||||
func (s *RepositoriesService) GetProject(owner, repo string, number int) (*Project, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/%v", owner, repo, number)
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
project := &Project{}
|
|
||||||
resp, err := s.client.Do(req, project)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return project, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectOptions specifies the parameters to the
|
|
||||||
// RepositoriesService.CreateProject and
|
|
||||||
// RepositoriesService.UpdateProject methods.
|
|
||||||
type ProjectOptions struct {
|
|
||||||
// The name of the project. (Required for creation; optional for update.)
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
// The body of the project. (Optional.)
|
|
||||||
Body string `json:"body,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateProject creates a GitHub Project for the specified repository.
|
// CreateProject creates a GitHub Project for the specified repository.
|
||||||
//
|
//
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-project
|
// GitHub API docs: https://developer.github.com/v3/projects/#create-a-repository-project
|
||||||
func (s *RepositoriesService) CreateProject(owner, repo string, projectOptions *ProjectOptions) (*Project, *Response, error) {
|
func (s *RepositoriesService) CreateProject(owner, repo string, opt *ProjectOptions) (*Project, *Response, error) {
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
|
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
|
||||||
req, err := s.client.NewRequest("POST", u, projectOptions)
|
req, err := s.client.NewRequest("POST", u, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -110,355 +55,3 @@ func (s *RepositoriesService) CreateProject(owner, repo string, projectOptions *
|
||||||
|
|
||||||
return project, resp, err
|
return project, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProject updates a repository project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#update-a-project
|
|
||||||
func (s *RepositoriesService) UpdateProject(owner, repo string, number int, projectOptions *ProjectOptions) (*Project, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/%v", owner, repo, number)
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, projectOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
project := &Project{}
|
|
||||||
resp, err := s.client.Do(req, project)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return project, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteProject deletes a GitHub Project from a repository.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#delete-a-project
|
|
||||||
func (s *RepositoriesService) DeleteProject(owner, repo string, number int) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/%v", owner, repo, number)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectColumn represents a column of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/
|
|
||||||
type ProjectColumn struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
ProjectURL *string `json:"project_url,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListProjectColumns lists the columns of a GitHub Project for a repo.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#list-columns
|
|
||||||
func (s *RepositoriesService) ListProjectColumns(owner, repo string, number int, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/%v/columns", owner, repo, number)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
columns := []*ProjectColumn{}
|
|
||||||
resp, err := s.client.Do(req, &columns)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetProjectColumn gets a column of a GitHub Project for a repo.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#get-a-column
|
|
||||||
func (s *RepositoriesService) GetProjectColumn(owner, repo string, columnID int) (*ProjectColumn, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v", owner, repo, columnID)
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
column := &ProjectColumn{}
|
|
||||||
resp, err := s.client.Do(req, column)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return column, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectColumnOptions specifies the parameters to the
|
|
||||||
// RepositoriesService.CreateProjectColumn and
|
|
||||||
// RepositoriesService.UpdateProjectColumn methods.
|
|
||||||
type ProjectColumnOptions struct {
|
|
||||||
// The name of the project column. (Required for creation and update.)
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateProjectColumn creates a column for the specified (by number) project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-column
|
|
||||||
func (s *RepositoriesService) CreateProjectColumn(owner, repo string, number int, columnOptions *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/%v/columns", owner, repo, number)
|
|
||||||
req, err := s.client.NewRequest("POST", u, columnOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
column := &ProjectColumn{}
|
|
||||||
resp, err := s.client.Do(req, column)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return column, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateProjectColumn updates a column of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#update-a-column
|
|
||||||
func (s *RepositoriesService) UpdateProjectColumn(owner, repo string, columnID int, columnOptions *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v", owner, repo, columnID)
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, columnOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
column := &ProjectColumn{}
|
|
||||||
resp, err := s.client.Do(req, column)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return column, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteProjectColumn deletes a column from a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#delete-a-column
|
|
||||||
func (s *RepositoriesService) DeleteProjectColumn(owner, repo string, columnID int) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v", owner, repo, columnID)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectColumnMoveOptions specifies the parameters to the
|
|
||||||
// RepositoriesService.MoveProjectColumn method.
|
|
||||||
type ProjectColumnMoveOptions struct {
|
|
||||||
// Position can be one of "first", "last", or "after:<column-id>", where
|
|
||||||
// <column-id> is the ID of a column in the same project. (Required.)
|
|
||||||
Position string `json:"position"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MoveProjectColumn moves a column within a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#move-a-column
|
|
||||||
func (s *RepositoriesService) MoveProjectColumn(owner, repo string, columnID int, moveOptions *ProjectColumnMoveOptions) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v/moves", owner, repo, columnID)
|
|
||||||
req, err := s.client.NewRequest("POST", u, moveOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectCard represents a card in a column of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/
|
|
||||||
type ProjectCard struct {
|
|
||||||
ColumnURL *string `json:"column_url,omitempty"`
|
|
||||||
ContentURL *string `json:"content_url,omitempty"`
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
Note *string `json:"note,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListProjectCards lists the cards in a column of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#list-projects-cards
|
|
||||||
func (s *RepositoriesService) ListProjectCards(owner, repo string, columnID int, opt *ListOptions) ([]*ProjectCard, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v/cards", owner, repo, columnID)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
cards := []*ProjectCard{}
|
|
||||||
resp, err := s.client.Do(req, &cards)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cards, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetProjectCard gets a card in a column of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#get-a-project-card
|
|
||||||
func (s *RepositoriesService) GetProjectCard(owner, repo string, columnID int) (*ProjectCard, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v", owner, repo, columnID)
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
card := &ProjectCard{}
|
|
||||||
resp, err := s.client.Do(req, card)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return card, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectCardOptions specifies the parameters to the
|
|
||||||
// RepositoriesService.CreateProjectCard and
|
|
||||||
// RepositoriesService.UpdateProjectCard methods.
|
|
||||||
type ProjectCardOptions struct {
|
|
||||||
// The note of the card. Note and ContentID are mutually exclusive.
|
|
||||||
Note string `json:"note,omitempty"`
|
|
||||||
// The ID (not Number) of the Issue or Pull Request to associate with this card.
|
|
||||||
// Note and ContentID are mutually exclusive.
|
|
||||||
ContentID int `json:"content_id,omitempty"`
|
|
||||||
// The type of content to associate with this card. Possible values are: "Issue", "PullRequest".
|
|
||||||
ContentType string `json:"content_type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateProjectCard creates a card in the specified column of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-project-card
|
|
||||||
func (s *RepositoriesService) CreateProjectCard(owner, repo string, columnID int, cardOptions *ProjectCardOptions) (*ProjectCard, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v/cards", owner, repo, columnID)
|
|
||||||
req, err := s.client.NewRequest("POST", u, cardOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
card := &ProjectCard{}
|
|
||||||
resp, err := s.client.Do(req, card)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return card, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateProjectCard updates a card of a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#update-a-project-card
|
|
||||||
func (s *RepositoriesService) UpdateProjectCard(owner, repo string, cardID int, cardOptions *ProjectCardOptions) (*ProjectCard, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v", owner, repo, cardID)
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, cardOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
card := &ProjectCard{}
|
|
||||||
resp, err := s.client.Do(req, card)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return card, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteProjectCard deletes a card from a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#delete-a-project-card
|
|
||||||
func (s *RepositoriesService) DeleteProjectCard(owner, repo string, cardID int) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v", owner, repo, cardID)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectCardMoveOptions specifies the parameters to the
|
|
||||||
// RepositoriesService.MoveProjectCard method.
|
|
||||||
type ProjectCardMoveOptions struct {
|
|
||||||
// Position can be one of "top", "bottom", or "after:<card-id>", where
|
|
||||||
// <card-id> is the ID of a card in the same project.
|
|
||||||
Position string `json:"position"`
|
|
||||||
// ColumnID is the ID of a column in the same project. Note that ColumnID
|
|
||||||
// is required when using Position "after:<card-id>" when that card is in
|
|
||||||
// another column; otherwise it is optional.
|
|
||||||
ColumnID int `json:"column_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MoveProjectCard moves a card within a GitHub Project.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#move-a-project-card
|
|
||||||
func (s *RepositoriesService) MoveProjectCard(owner, repo string, cardID int, moveOptions *ProjectCardMoveOptions) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v/moves", owner, repo, cardID)
|
|
||||||
req, err := s.client.NewRequest("POST", u, moveOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches.
|
|
||||||
req.Header.Set("Accept", mediaTypeProjectsPreview)
|
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
|
||||||
}
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ type AgentServiceCheck struct {
|
||||||
HTTP string `json:",omitempty"`
|
HTTP string `json:",omitempty"`
|
||||||
TCP string `json:",omitempty"`
|
TCP string `json:",omitempty"`
|
||||||
Status string `json:",omitempty"`
|
Status string `json:",omitempty"`
|
||||||
|
TLSSkipVerify string `json:",omitempty"`
|
||||||
|
|
||||||
// In Consul 0.7 and later, checks that are associated with a service
|
// In Consul 0.7 and later, checks that are associated with a service
|
||||||
// may also contain this optional DeregisterCriticalServiceAfter field,
|
// may also contain this optional DeregisterCriticalServiceAfter field,
|
||||||
|
|
|
@ -156,7 +156,7 @@ func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
|
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
|
||||||
r := k.c.newRequest("GET", "/v1/kv/"+key)
|
r := k.c.newRequest("GET", "/v1/kv/"+strings.TrimPrefix(key, "/"))
|
||||||
r.setQueryOptions(q)
|
r.setQueryOptions(q)
|
||||||
for param, val := range params {
|
for param, val := range params {
|
||||||
r.params.Set(param, val)
|
r.params.Set(param, val)
|
||||||
|
@ -277,7 +277,7 @@ func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
|
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
|
||||||
r := k.c.newRequest("DELETE", "/v1/kv/"+key)
|
r := k.c.newRequest("DELETE", "/v1/kv/"+strings.TrimPrefix(key, "/"))
|
||||||
r.setWriteOptions(q)
|
r.setWriteOptions(q)
|
||||||
for param, val := range params {
|
for param, val := range params {
|
||||||
r.params.Set(param, val)
|
r.params.Set(param, val)
|
||||||
|
|
|
@ -167,19 +167,18 @@ func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete is used to delete a specific prepared query.
|
// Delete is used to delete a specific prepared query.
|
||||||
func (c *PreparedQuery) Delete(queryID string, q *QueryOptions) (*QueryMeta, error) {
|
func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
|
r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
|
||||||
r.setQueryOptions(q)
|
r.setWriteOptions(q)
|
||||||
rtt, resp, err := requireOK(c.c.doRequest(r))
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
qm := &QueryMeta{}
|
wm := &WriteMeta{}
|
||||||
parseQueryMeta(resp, qm)
|
wm.RequestTime = rtt
|
||||||
qm.RequestTime = rtt
|
return wm, nil
|
||||||
return qm, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute is used to execute a specific prepared query. You can execute using
|
// Execute is used to execute a specific prepared query. You can execute using
|
||||||
|
|
|
@ -12,12 +12,16 @@ type ErrorFormatFunc func([]error) string
|
||||||
// ListFormatFunc is a basic formatter that outputs the number of errors
|
// ListFormatFunc is a basic formatter that outputs the number of errors
|
||||||
// that occurred along with a bullet point list of the errors.
|
// that occurred along with a bullet point list of the errors.
|
||||||
func ListFormatFunc(es []error) string {
|
func ListFormatFunc(es []error) string {
|
||||||
|
if len(es) == 1 {
|
||||||
|
return fmt.Sprintf("1 error occurred:\n\n* %s", es[0])
|
||||||
|
}
|
||||||
|
|
||||||
points := make([]string, len(es))
|
points := make([]string, len(es))
|
||||||
for i, err := range es {
|
for i, err := range es {
|
||||||
points[i] = fmt.Sprintf("* %s", err)
|
points[i] = fmt.Sprintf("* %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"%d error(s) occurred:\n\n%s",
|
"%d errors occurred:\n\n%s",
|
||||||
len(es), strings.Join(points, "\n"))
|
len(es), strings.Join(points, "\n"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func Unquote(s string) (t string, err error) {
|
||||||
for len(s) > 0 {
|
for len(s) > 0 {
|
||||||
// If we're starting a '${}' then let it through un-unquoted.
|
// If we're starting a '${}' then let it through un-unquoted.
|
||||||
// Specifically: we don't unquote any characters within the `${}`
|
// Specifically: we don't unquote any characters within the `${}`
|
||||||
// section, except for escaped backslashes, which we handle specifically.
|
// section.
|
||||||
if s[0] == '$' && len(s) > 1 && s[1] == '{' {
|
if s[0] == '$' && len(s) > 1 && s[1] == '{' {
|
||||||
buf = append(buf, '$', '{')
|
buf = append(buf, '$', '{')
|
||||||
s = s[2:]
|
s = s[2:]
|
||||||
|
@ -61,16 +61,6 @@ func Unquote(s string) (t string, err error) {
|
||||||
|
|
||||||
s = s[size:]
|
s = s[size:]
|
||||||
|
|
||||||
// We special case escaped backslashes in interpolations, converting
|
|
||||||
// them to their unescaped equivalents.
|
|
||||||
if r == '\\' {
|
|
||||||
q, _ := utf8.DecodeRuneInString(s)
|
|
||||||
switch q {
|
|
||||||
case '\\':
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n := utf8.EncodeRune(runeTmp[:], r)
|
n := utf8.EncodeRune(runeTmp[:], r)
|
||||||
buf = append(buf, runeTmp[:n]...)
|
buf = append(buf, runeTmp[:n]...)
|
||||||
|
|
||||||
|
|
|
@ -1507,12 +1507,23 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
|
||||||
dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i], rs.colFmts[i])
|
dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i], rs.colFmts[i])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
case 'T':
|
||||||
|
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb)
|
||||||
|
return io.EOF
|
||||||
default:
|
default:
|
||||||
errorf("unexpected message after execute: %q", t)
|
errorf("unexpected message after execute: %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *rows) HasNextResultSet() bool {
|
||||||
|
return !rs.done
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *rows) NextResultSet() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
|
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
|
||||||
// used as part of an SQL statement. For example:
|
// used as part of an SQL statement. For example:
|
||||||
//
|
//
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence.
|
||||||
func NewColorable(file *os.File) io.Writer {
|
func NewColorable(file *os.File) io.Writer {
|
||||||
if file == nil {
|
if file == nil {
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
@ -15,10 +16,12 @@ func NewColorable(file *os.File) io.Writer {
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
func NewColorableStdout() io.Writer {
|
func NewColorableStdout() io.Writer {
|
||||||
return os.Stdout
|
return os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
func NewColorableStderr() io.Writer {
|
func NewColorableStderr() io.Writer {
|
||||||
return os.Stderr
|
return os.Stderr
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ type Writer struct {
|
||||||
oldpos coord
|
oldpos coord
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||||
func NewColorable(file *os.File) io.Writer {
|
func NewColorable(file *os.File) io.Writer {
|
||||||
if file == nil {
|
if file == nil {
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
@ -90,10 +91,12 @@ func NewColorable(file *os.File) io.Writer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
func NewColorableStdout() io.Writer {
|
func NewColorableStdout() io.Writer {
|
||||||
return NewColorable(os.Stdout)
|
return NewColorable(os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
func NewColorableStderr() io.Writer {
|
func NewColorableStderr() io.Writer {
|
||||||
return NewColorable(os.Stderr)
|
return NewColorable(os.Stderr)
|
||||||
}
|
}
|
||||||
|
@ -357,6 +360,7 @@ var color256 = map[int]int{
|
||||||
255: 0xeeeeee,
|
255: 0xeeeeee,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write write data on console
|
||||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
var csbi consoleScreenBufferInfo
|
var csbi consoleScreenBufferInfo
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
|
|
@ -5,15 +5,18 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NonColorable hold writer but remove escape sequence.
|
||||||
type NonColorable struct {
|
type NonColorable struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
lastbuf bytes.Buffer
|
lastbuf bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
|
||||||
func NewNonColorable(w io.Writer) io.Writer {
|
func NewNonColorable(w io.Writer) io.Writer {
|
||||||
return &NonColorable{out: w}
|
return &NonColorable{out: w}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write write data on console
|
||||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||||
er := bytes.NewReader(data)
|
er := bytes.NewReader(data)
|
||||||
var bw [1]byte
|
var bw [1]byte
|
||||||
|
|
|
@ -36,6 +36,103 @@
|
||||||
// Contexts.
|
// Contexts.
|
||||||
package context // import "golang.org/x/net/context"
|
package context // import "golang.org/x/net/context"
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// A Context carries a deadline, a cancelation signal, and other values across
|
||||||
|
// API boundaries.
|
||||||
|
//
|
||||||
|
// Context's methods may be called by multiple goroutines simultaneously.
|
||||||
|
type Context interface {
|
||||||
|
// Deadline returns the time when work done on behalf of this context
|
||||||
|
// should be canceled. Deadline returns ok==false when no deadline is
|
||||||
|
// set. Successive calls to Deadline return the same results.
|
||||||
|
Deadline() (deadline time.Time, ok bool)
|
||||||
|
|
||||||
|
// Done returns a channel that's closed when work done on behalf of this
|
||||||
|
// context should be canceled. Done may return nil if this context can
|
||||||
|
// never be canceled. Successive calls to Done return the same value.
|
||||||
|
//
|
||||||
|
// WithCancel arranges for Done to be closed when cancel is called;
|
||||||
|
// WithDeadline arranges for Done to be closed when the deadline
|
||||||
|
// expires; WithTimeout arranges for Done to be closed when the timeout
|
||||||
|
// elapses.
|
||||||
|
//
|
||||||
|
// Done is provided for use in select statements:
|
||||||
|
//
|
||||||
|
// // Stream generates values with DoSomething and sends them to out
|
||||||
|
// // until DoSomething returns an error or ctx.Done is closed.
|
||||||
|
// func Stream(ctx context.Context, out chan<- Value) error {
|
||||||
|
// for {
|
||||||
|
// v, err := DoSomething(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// select {
|
||||||
|
// case <-ctx.Done():
|
||||||
|
// return ctx.Err()
|
||||||
|
// case out <- v:
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// See http://blog.golang.org/pipelines for more examples of how to use
|
||||||
|
// a Done channel for cancelation.
|
||||||
|
Done() <-chan struct{}
|
||||||
|
|
||||||
|
// Err returns a non-nil error value after Done is closed. Err returns
|
||||||
|
// Canceled if the context was canceled or DeadlineExceeded if the
|
||||||
|
// context's deadline passed. No other values for Err are defined.
|
||||||
|
// After Done is closed, successive calls to Err return the same value.
|
||||||
|
Err() error
|
||||||
|
|
||||||
|
// Value returns the value associated with this context for key, or nil
|
||||||
|
// if no value is associated with key. Successive calls to Value with
|
||||||
|
// the same key returns the same result.
|
||||||
|
//
|
||||||
|
// Use context values only for request-scoped data that transits
|
||||||
|
// processes and API boundaries, not for passing optional parameters to
|
||||||
|
// functions.
|
||||||
|
//
|
||||||
|
// A key identifies a specific value in a Context. Functions that wish
|
||||||
|
// to store values in Context typically allocate a key in a global
|
||||||
|
// variable then use that key as the argument to context.WithValue and
|
||||||
|
// Context.Value. A key can be any type that supports equality;
|
||||||
|
// packages should define keys as an unexported type to avoid
|
||||||
|
// collisions.
|
||||||
|
//
|
||||||
|
// Packages that define a Context key should provide type-safe accessors
|
||||||
|
// for the values stores using that key:
|
||||||
|
//
|
||||||
|
// // Package user defines a User type that's stored in Contexts.
|
||||||
|
// package user
|
||||||
|
//
|
||||||
|
// import "golang.org/x/net/context"
|
||||||
|
//
|
||||||
|
// // User is the type of value stored in the Contexts.
|
||||||
|
// type User struct {...}
|
||||||
|
//
|
||||||
|
// // key is an unexported type for keys defined in this package.
|
||||||
|
// // This prevents collisions with keys defined in other packages.
|
||||||
|
// type key int
|
||||||
|
//
|
||||||
|
// // userKey is the key for user.User values in Contexts. It is
|
||||||
|
// // unexported; clients use user.NewContext and user.FromContext
|
||||||
|
// // instead of using this key directly.
|
||||||
|
// var userKey key = 0
|
||||||
|
//
|
||||||
|
// // NewContext returns a new Context that carries value u.
|
||||||
|
// func NewContext(ctx context.Context, u *User) context.Context {
|
||||||
|
// return context.WithValue(ctx, userKey, u)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // FromContext returns the User value stored in ctx, if any.
|
||||||
|
// func FromContext(ctx context.Context) (*User, bool) {
|
||||||
|
// u, ok := ctx.Value(userKey).(*User)
|
||||||
|
// return u, ok
|
||||||
|
// }
|
||||||
|
Value(key interface{}) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
// Background returns a non-nil, empty Context. It is never canceled, has no
|
||||||
// values, and has no deadline. It is typically used by the main function,
|
// values, and has no deadline. It is typically used by the main function,
|
||||||
// initialization, and tests, and as the top-level Context for incoming
|
// initialization, and tests, and as the top-level Context for incoming
|
||||||
|
@ -52,3 +149,8 @@ func Background() Context {
|
||||||
func TODO() Context {
|
func TODO() Context {
|
||||||
return todo
|
return todo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A CancelFunc tells an operation to abandon its work.
|
||||||
|
// A CancelFunc does not wait for the work to stop.
|
||||||
|
// After the first call, subsequent calls to a CancelFunc do nothing.
|
||||||
|
type CancelFunc func()
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build go1.8
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context" // standard library's context, as of Go 1.7
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Context carries a deadline, a cancelation signal, and other values across
|
|
||||||
// API boundaries.
|
|
||||||
//
|
|
||||||
// Context's methods may be called by multiple goroutines simultaneously.
|
|
||||||
type Context => context.Context
|
|
||||||
|
|
||||||
// A CancelFunc tells an operation to abandon its work.
|
|
||||||
// A CancelFunc does not wait for the work to stop.
|
|
||||||
// After the first call, subsequent calls to a CancelFunc do nothing.
|
|
||||||
type CancelFunc => context.CancelFunc
|
|
|
@ -1,109 +0,0 @@
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !go1.8
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// A CancelFunc tells an operation to abandon its work.
|
|
||||||
// A CancelFunc does not wait for the work to stop.
|
|
||||||
// After the first call, subsequent calls to a CancelFunc do nothing.
|
|
||||||
type CancelFunc func()
|
|
||||||
|
|
||||||
// A Context carries a deadline, a cancelation signal, and other values across
|
|
||||||
// API boundaries.
|
|
||||||
//
|
|
||||||
// Context's methods may be called by multiple goroutines simultaneously.
|
|
||||||
type Context interface {
|
|
||||||
// Deadline returns the time when work done on behalf of this context
|
|
||||||
// should be canceled. Deadline returns ok==false when no deadline is
|
|
||||||
// set. Successive calls to Deadline return the same results.
|
|
||||||
Deadline() (deadline time.Time, ok bool)
|
|
||||||
|
|
||||||
// Done returns a channel that's closed when work done on behalf of this
|
|
||||||
// context should be canceled. Done may return nil if this context can
|
|
||||||
// never be canceled. Successive calls to Done return the same value.
|
|
||||||
//
|
|
||||||
// WithCancel arranges for Done to be closed when cancel is called;
|
|
||||||
// WithDeadline arranges for Done to be closed when the deadline
|
|
||||||
// expires; WithTimeout arranges for Done to be closed when the timeout
|
|
||||||
// elapses.
|
|
||||||
//
|
|
||||||
// Done is provided for use in select statements:
|
|
||||||
//
|
|
||||||
// // Stream generates values with DoSomething and sends them to out
|
|
||||||
// // until DoSomething returns an error or ctx.Done is closed.
|
|
||||||
// func Stream(ctx context.Context, out chan<- Value) error {
|
|
||||||
// for {
|
|
||||||
// v, err := DoSomething(ctx)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// select {
|
|
||||||
// case <-ctx.Done():
|
|
||||||
// return ctx.Err()
|
|
||||||
// case out <- v:
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// See http://blog.golang.org/pipelines for more examples of how to use
|
|
||||||
// a Done channel for cancelation.
|
|
||||||
Done() <-chan struct{}
|
|
||||||
|
|
||||||
// Err returns a non-nil error value after Done is closed. Err returns
|
|
||||||
// Canceled if the context was canceled or DeadlineExceeded if the
|
|
||||||
// context's deadline passed. No other values for Err are defined.
|
|
||||||
// After Done is closed, successive calls to Err return the same value.
|
|
||||||
Err() error
|
|
||||||
|
|
||||||
// Value returns the value associated with this context for key, or nil
|
|
||||||
// if no value is associated with key. Successive calls to Value with
|
|
||||||
// the same key returns the same result.
|
|
||||||
//
|
|
||||||
// Use context values only for request-scoped data that transits
|
|
||||||
// processes and API boundaries, not for passing optional parameters to
|
|
||||||
// functions.
|
|
||||||
//
|
|
||||||
// A key identifies a specific value in a Context. Functions that wish
|
|
||||||
// to store values in Context typically allocate a key in a global
|
|
||||||
// variable then use that key as the argument to context.WithValue and
|
|
||||||
// Context.Value. A key can be any type that supports equality;
|
|
||||||
// packages should define keys as an unexported type to avoid
|
|
||||||
// collisions.
|
|
||||||
//
|
|
||||||
// Packages that define a Context key should provide type-safe accessors
|
|
||||||
// for the values stores using that key:
|
|
||||||
//
|
|
||||||
// // Package user defines a User type that's stored in Contexts.
|
|
||||||
// package user
|
|
||||||
//
|
|
||||||
// import "golang.org/x/net/context"
|
|
||||||
//
|
|
||||||
// // User is the type of value stored in the Contexts.
|
|
||||||
// type User struct {...}
|
|
||||||
//
|
|
||||||
// // key is an unexported type for keys defined in this package.
|
|
||||||
// // This prevents collisions with keys defined in other packages.
|
|
||||||
// type key int
|
|
||||||
//
|
|
||||||
// // userKey is the key for user.User values in Contexts. It is
|
|
||||||
// // unexported; clients use user.NewContext and user.FromContext
|
|
||||||
// // instead of using this key directly.
|
|
||||||
// var userKey key = 0
|
|
||||||
//
|
|
||||||
// // NewContext returns a new Context that carries value u.
|
|
||||||
// func NewContext(ctx context.Context, u *User) context.Context {
|
|
||||||
// return context.WithValue(ctx, userKey, u)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // FromContext returns the User value stored in ctx, if any.
|
|
||||||
// func FromContext(ctx context.Context) (*User, bool) {
|
|
||||||
// u, ok := ctx.Value(userKey).(*User)
|
|
||||||
// return u, ok
|
|
||||||
// }
|
|
||||||
Value(key interface{}) interface{}
|
|
||||||
}
|
|
|
@ -35,3 +35,7 @@ func configureServer18(h1 *http.Server, h2 *Server) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldLogPanic(panicValue interface{}) bool {
|
||||||
|
return panicValue != nil && panicValue != http.ErrAbortHandler
|
||||||
|
}
|
||||||
|
|
|
@ -12,3 +12,7 @@ func configureServer18(h1 *http.Server, h2 *Server) error {
|
||||||
// No IdleTimeout to sync prior to Go 1.8.
|
// No IdleTimeout to sync prior to Go 1.8.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldLogPanic(panicValue interface{}) bool {
|
||||||
|
return panicValue != nil
|
||||||
|
}
|
||||||
|
|
|
@ -188,9 +188,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
||||||
if !haveNPN {
|
if !haveNPN {
|
||||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
|
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
|
||||||
}
|
}
|
||||||
// h2-14 is temporary (as of 2015-03-05) while we wait for all browsers
|
|
||||||
// to switch to "h2".
|
|
||||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2-14")
|
|
||||||
|
|
||||||
if s.TLSNextProto == nil {
|
if s.TLSNextProto == nil {
|
||||||
s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
|
s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
|
||||||
|
@ -205,7 +202,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
s.TLSNextProto[NextProtoTLS] = protoHandler
|
s.TLSNextProto[NextProtoTLS] = protoHandler
|
||||||
s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,8 +386,8 @@ type serverConn struct {
|
||||||
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
|
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
|
||||||
curClientStreams uint32 // number of open streams initiated by the client
|
curClientStreams uint32 // number of open streams initiated by the client
|
||||||
curPushedStreams uint32 // number of open streams initiated by server push
|
curPushedStreams uint32 // number of open streams initiated by server push
|
||||||
maxStreamID uint32 // max ever seen from client
|
maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests
|
||||||
maxPushPromiseID uint32 // ID of the last push promise, or 0 if there have been no pushes
|
maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes
|
||||||
streams map[uint32]*stream
|
streams map[uint32]*stream
|
||||||
initialWindowSize int32
|
initialWindowSize int32
|
||||||
maxFrameSize int32
|
maxFrameSize int32
|
||||||
|
@ -481,8 +477,14 @@ func (sc *serverConn) state(streamID uint32) (streamState, *stream) {
|
||||||
// a client sends a HEADERS frame on stream 7 without ever sending a
|
// a client sends a HEADERS frame on stream 7 without ever sending a
|
||||||
// frame on stream 5, then stream 5 transitions to the "closed"
|
// frame on stream 5, then stream 5 transitions to the "closed"
|
||||||
// state when the first frame for stream 7 is sent or received."
|
// state when the first frame for stream 7 is sent or received."
|
||||||
if streamID <= sc.maxStreamID {
|
if streamID%2 == 1 {
|
||||||
return stateClosed, nil
|
if streamID <= sc.maxClientStreamID {
|
||||||
|
return stateClosed, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if streamID <= sc.maxPushPromiseID {
|
||||||
|
return stateClosed, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return stateIdle, nil
|
return stateIdle, nil
|
||||||
}
|
}
|
||||||
|
@ -740,7 +742,7 @@ func (sc *serverConn) serve() {
|
||||||
return
|
return
|
||||||
case <-gracefulShutdownCh:
|
case <-gracefulShutdownCh:
|
||||||
gracefulShutdownCh = nil
|
gracefulShutdownCh = nil
|
||||||
sc.goAwayIn(ErrCodeNo, 0)
|
sc.startGracefulShutdown()
|
||||||
case <-sc.shutdownTimerCh:
|
case <-sc.shutdownTimerCh:
|
||||||
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
|
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
|
||||||
return
|
return
|
||||||
|
@ -1015,7 +1017,7 @@ func (sc *serverConn) scheduleFrameWrite() {
|
||||||
sc.needToSendGoAway = false
|
sc.needToSendGoAway = false
|
||||||
sc.startFrameWrite(FrameWriteRequest{
|
sc.startFrameWrite(FrameWriteRequest{
|
||||||
write: &writeGoAway{
|
write: &writeGoAway{
|
||||||
maxStreamID: sc.maxStreamID,
|
maxStreamID: sc.maxClientStreamID,
|
||||||
code: sc.goAwayCode,
|
code: sc.goAwayCode,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1042,6 +1044,13 @@ func (sc *serverConn) scheduleFrameWrite() {
|
||||||
sc.inFrameScheduleLoop = false
|
sc.inFrameScheduleLoop = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startGracefulShutdown sends a GOAWAY with ErrCodeNo to tell the
|
||||||
|
// client we're gracefully shutting down. The connection isn't closed
|
||||||
|
// until all current streams are done.
|
||||||
|
func (sc *serverConn) startGracefulShutdown() {
|
||||||
|
sc.goAwayIn(ErrCodeNo, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *serverConn) goAway(code ErrCode) {
|
func (sc *serverConn) goAway(code ErrCode) {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
var forceCloseIn time.Duration
|
var forceCloseIn time.Duration
|
||||||
|
@ -1164,6 +1173,8 @@ func (sc *serverConn) processFrame(f Frame) error {
|
||||||
return sc.processResetStream(f)
|
return sc.processResetStream(f)
|
||||||
case *PriorityFrame:
|
case *PriorityFrame:
|
||||||
return sc.processPriority(f)
|
return sc.processPriority(f)
|
||||||
|
case *GoAwayFrame:
|
||||||
|
return sc.processGoAway(f)
|
||||||
case *PushPromiseFrame:
|
case *PushPromiseFrame:
|
||||||
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
|
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
|
||||||
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||||
|
@ -1189,7 +1200,7 @@ func (sc *serverConn) processPing(f *PingFrame) error {
|
||||||
// PROTOCOL_ERROR."
|
// PROTOCOL_ERROR."
|
||||||
return ConnectionError(ErrCodeProtocol)
|
return ConnectionError(ErrCodeProtocol)
|
||||||
}
|
}
|
||||||
if sc.inGoAway {
|
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
|
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
|
||||||
|
@ -1198,9 +1209,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
|
||||||
|
|
||||||
func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
if sc.inGoAway {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case f.StreamID != 0: // stream-level flow control
|
case f.StreamID != 0: // stream-level flow control
|
||||||
state, st := sc.state(f.StreamID)
|
state, st := sc.state(f.StreamID)
|
||||||
|
@ -1233,9 +1241,6 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
||||||
|
|
||||||
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
|
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
if sc.inGoAway {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
state, st := sc.state(f.StreamID)
|
state, st := sc.state(f.StreamID)
|
||||||
if state == stateIdle {
|
if state == stateIdle {
|
||||||
|
@ -1265,12 +1270,15 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
||||||
} else {
|
} else {
|
||||||
sc.curClientStreams--
|
sc.curClientStreams--
|
||||||
}
|
}
|
||||||
if sc.curClientStreams+sc.curPushedStreams == 0 {
|
|
||||||
sc.setConnState(http.StateIdle)
|
|
||||||
}
|
|
||||||
delete(sc.streams, st.id)
|
delete(sc.streams, st.id)
|
||||||
if len(sc.streams) == 0 && sc.srv.IdleTimeout != 0 {
|
if len(sc.streams) == 0 {
|
||||||
sc.idleTimer.Reset(sc.srv.IdleTimeout)
|
sc.setConnState(http.StateIdle)
|
||||||
|
if sc.srv.IdleTimeout != 0 {
|
||||||
|
sc.idleTimer.Reset(sc.srv.IdleTimeout)
|
||||||
|
}
|
||||||
|
if h1ServerKeepAlivesDisabled(sc.hs) {
|
||||||
|
sc.startGracefulShutdown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if p := st.body; p != nil {
|
if p := st.body; p != nil {
|
||||||
// Return any buffered unread bytes worth of conn-level flow control.
|
// Return any buffered unread bytes worth of conn-level flow control.
|
||||||
|
@ -1295,9 +1303,6 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if sc.inGoAway {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1369,7 +1374,7 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
|
||||||
|
|
||||||
func (sc *serverConn) processData(f *DataFrame) error {
|
func (sc *serverConn) processData(f *DataFrame) error {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
if sc.inGoAway {
|
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data := f.Data()
|
data := f.Data()
|
||||||
|
@ -1448,6 +1453,20 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) processGoAway(f *GoAwayFrame) error {
|
||||||
|
sc.serveG.check()
|
||||||
|
if f.ErrCode != ErrCodeNo {
|
||||||
|
sc.logf("http2: received GOAWAY %+v, starting graceful shutdown", f)
|
||||||
|
} else {
|
||||||
|
sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
|
||||||
|
}
|
||||||
|
sc.startGracefulShutdown()
|
||||||
|
// http://tools.ietf.org/html/rfc7540#section-6.8
|
||||||
|
// We should not create any new streams, which means we should disable push.
|
||||||
|
sc.pushEnabled = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// isPushed reports whether the stream is server-initiated.
|
// isPushed reports whether the stream is server-initiated.
|
||||||
func (st *stream) isPushed() bool {
|
func (st *stream) isPushed() bool {
|
||||||
return st.id%2 == 0
|
return st.id%2 == 0
|
||||||
|
@ -1508,10 +1527,10 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||||
// endpoint has opened or reserved. [...] An endpoint that
|
// endpoint has opened or reserved. [...] An endpoint that
|
||||||
// receives an unexpected stream identifier MUST respond with
|
// receives an unexpected stream identifier MUST respond with
|
||||||
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||||
if id <= sc.maxStreamID {
|
if id <= sc.maxClientStreamID {
|
||||||
return ConnectionError(ErrCodeProtocol)
|
return ConnectionError(ErrCodeProtocol)
|
||||||
}
|
}
|
||||||
sc.maxStreamID = id
|
sc.maxClientStreamID = id
|
||||||
|
|
||||||
if sc.idleTimer != nil {
|
if sc.idleTimer != nil {
|
||||||
sc.idleTimer.Stop()
|
sc.idleTimer.Stop()
|
||||||
|
@ -1847,15 +1866,17 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
|
||||||
rw.rws.stream.cancelCtx()
|
rw.rws.stream.cancelCtx()
|
||||||
if didPanic {
|
if didPanic {
|
||||||
e := recover()
|
e := recover()
|
||||||
// Same as net/http:
|
|
||||||
const size = 64 << 10
|
|
||||||
buf := make([]byte, size)
|
|
||||||
buf = buf[:runtime.Stack(buf, false)]
|
|
||||||
sc.writeFrameFromHandler(FrameWriteRequest{
|
sc.writeFrameFromHandler(FrameWriteRequest{
|
||||||
write: handlerPanicRST{rw.rws.stream.id},
|
write: handlerPanicRST{rw.rws.stream.id},
|
||||||
stream: rw.rws.stream,
|
stream: rw.rws.stream,
|
||||||
})
|
})
|
||||||
sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
|
// Same as net/http:
|
||||||
|
if shouldLogPanic(e) {
|
||||||
|
const size = 64 << 10
|
||||||
|
buf := make([]byte, size)
|
||||||
|
buf = buf[:runtime.Stack(buf, false)]
|
||||||
|
sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rw.handlerDone()
|
rw.handlerDone()
|
||||||
|
@ -2267,8 +2288,9 @@ func (w *responseWriter) CloseNotify() <-chan bool {
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
ch = make(chan bool, 1)
|
ch = make(chan bool, 1)
|
||||||
rws.closeNotifierCh = ch
|
rws.closeNotifierCh = ch
|
||||||
|
cw := rws.stream.cw
|
||||||
go func() {
|
go func() {
|
||||||
rws.stream.cw.Wait() // wait for close
|
cw.Wait() // wait for close
|
||||||
ch <- true
|
ch <- true
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -2421,7 +2443,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
|
||||||
}
|
}
|
||||||
for k := range opts.Header {
|
for k := range opts.Header {
|
||||||
if strings.HasPrefix(k, ":") {
|
if strings.HasPrefix(k, ":") {
|
||||||
return fmt.Errorf("promised request headers cannot include psuedo header %q", k)
|
return fmt.Errorf("promised request headers cannot include pseudo header %q", k)
|
||||||
}
|
}
|
||||||
// These headers are meaningful only if the request has a body,
|
// These headers are meaningful only if the request has a body,
|
||||||
// but PUSH_PROMISE requests cannot have a body.
|
// but PUSH_PROMISE requests cannot have a body.
|
||||||
|
@ -2514,6 +2536,12 @@ func (sc *serverConn) startPush(msg startPushRequest) {
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc7540#section-5.1.1.
|
// http://tools.ietf.org/html/rfc7540#section-5.1.1.
|
||||||
// Streams initiated by the server MUST use even-numbered identifiers.
|
// Streams initiated by the server MUST use even-numbered identifiers.
|
||||||
|
// A server that is unable to establish a new stream identifier can send a GOAWAY
|
||||||
|
// frame so that the client is forced to open a new connection for new streams.
|
||||||
|
if sc.maxPushPromiseID+2 >= 1<<31 {
|
||||||
|
sc.startGracefulShutdown()
|
||||||
|
return 0, ErrPushLimitReached
|
||||||
|
}
|
||||||
sc.maxPushPromiseID += 2
|
sc.maxPushPromiseID += 2
|
||||||
promisedID := sc.maxPushPromiseID
|
promisedID := sc.maxPushPromiseID
|
||||||
|
|
||||||
|
@ -2660,3 +2688,17 @@ func h1ServerShutdownChan(hs *http.Server) <-chan struct{} {
|
||||||
|
|
||||||
// optional test hook for h1ServerShutdownChan.
|
// optional test hook for h1ServerShutdownChan.
|
||||||
var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{}
|
var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{}
|
||||||
|
|
||||||
|
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
||||||
|
// disabled. See comments on h1ServerShutdownChan above for why
|
||||||
|
// the code is written this way.
|
||||||
|
func h1ServerKeepAlivesDisabled(hs *http.Server) bool {
|
||||||
|
var x interface{} = hs
|
||||||
|
type I interface {
|
||||||
|
doKeepAlives() bool
|
||||||
|
}
|
||||||
|
if hs, ok := x.(I); ok {
|
||||||
|
return !hs.doKeepAlives()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -199,6 +199,7 @@ type clientStream struct {
|
||||||
bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read
|
bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read
|
||||||
readErr error // sticky read error; owned by transportResponseBody.Read
|
readErr error // sticky read error; owned by transportResponseBody.Read
|
||||||
stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu
|
stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu
|
||||||
|
didReset bool // whether we sent a RST_STREAM to the server; guarded by cc.mu
|
||||||
|
|
||||||
peerReset chan struct{} // closed on peer reset
|
peerReset chan struct{} // closed on peer reset
|
||||||
resetErr error // populated before peerReset is closed
|
resetErr error // populated before peerReset is closed
|
||||||
|
@ -226,15 +227,26 @@ func (cs *clientStream) awaitRequestCancel(req *http.Request) {
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-req.Cancel:
|
case <-req.Cancel:
|
||||||
|
cs.cancelStream()
|
||||||
cs.bufPipe.CloseWithError(errRequestCanceled)
|
cs.bufPipe.CloseWithError(errRequestCanceled)
|
||||||
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
cs.cancelStream()
|
||||||
cs.bufPipe.CloseWithError(ctx.Err())
|
cs.bufPipe.CloseWithError(ctx.Err())
|
||||||
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
|
|
||||||
case <-cs.done:
|
case <-cs.done:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cs *clientStream) cancelStream() {
|
||||||
|
cs.cc.mu.Lock()
|
||||||
|
didReset := cs.didReset
|
||||||
|
cs.didReset = true
|
||||||
|
cs.cc.mu.Unlock()
|
||||||
|
|
||||||
|
if !didReset {
|
||||||
|
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// checkResetOrDone reports any error sent in a RST_STREAM frame by the
|
// checkResetOrDone reports any error sent in a RST_STREAM frame by the
|
||||||
// server, or errStreamClosed if the stream is complete.
|
// server, or errStreamClosed if the stream is complete.
|
||||||
func (cs *clientStream) checkResetOrDone() error {
|
func (cs *clientStream) checkResetOrDone() error {
|
||||||
|
@ -1666,9 +1678,10 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
||||||
cc.bw.Flush()
|
cc.bw.Flush()
|
||||||
cc.wmu.Unlock()
|
cc.wmu.Unlock()
|
||||||
}
|
}
|
||||||
|
didReset := cs.didReset
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
|
||||||
if len(data) > 0 {
|
if len(data) > 0 && !didReset {
|
||||||
if _, err := cs.bufPipe.Write(data); err != nil {
|
if _, err := cs.bufPipe.Write(data); err != nil {
|
||||||
rl.endStreamError(cs, err)
|
rl.endStreamError(cs, err)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -25,7 +25,9 @@ type WriteScheduler interface {
|
||||||
// https://tools.ietf.org/html/rfc7540#section-5.1
|
// https://tools.ietf.org/html/rfc7540#section-5.1
|
||||||
AdjustStream(streamID uint32, priority PriorityParam)
|
AdjustStream(streamID uint32, priority PriorityParam)
|
||||||
|
|
||||||
// Push queues a frame in the scheduler.
|
// Push queues a frame in the scheduler. In most cases, this will not be
|
||||||
|
// called with wr.StreamID()!=0 unless that stream is currently open. The one
|
||||||
|
// exception is RST_STREAM frames, which may be sent on idle or closed streams.
|
||||||
Push(wr FrameWriteRequest)
|
Push(wr FrameWriteRequest)
|
||||||
|
|
||||||
// Pop dequeues the next frame to write. Returns false if no frames can
|
// Pop dequeues the next frame to write. Returns false if no frames can
|
||||||
|
@ -62,6 +64,13 @@ type FrameWriteRequest struct {
|
||||||
// 0 is used for non-stream frames such as PING and SETTINGS.
|
// 0 is used for non-stream frames such as PING and SETTINGS.
|
||||||
func (wr FrameWriteRequest) StreamID() uint32 {
|
func (wr FrameWriteRequest) StreamID() uint32 {
|
||||||
if wr.stream == nil {
|
if wr.stream == nil {
|
||||||
|
if se, ok := wr.write.(StreamError); ok {
|
||||||
|
// (*serverConn).resetStream doesn't set
|
||||||
|
// stream because it doesn't necessarily have
|
||||||
|
// one. So special case this type of write
|
||||||
|
// message.
|
||||||
|
return se.StreamID
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return wr.stream.id
|
return wr.stream.id
|
||||||
|
@ -142,17 +151,13 @@ func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteReque
|
||||||
|
|
||||||
// String is for debugging only.
|
// String is for debugging only.
|
||||||
func (wr FrameWriteRequest) String() string {
|
func (wr FrameWriteRequest) String() string {
|
||||||
var streamID uint32
|
|
||||||
if wr.stream != nil {
|
|
||||||
streamID = wr.stream.id
|
|
||||||
}
|
|
||||||
var des string
|
var des string
|
||||||
if s, ok := wr.write.(fmt.Stringer); ok {
|
if s, ok := wr.write.(fmt.Stringer); ok {
|
||||||
des = s.String()
|
des = s.String()
|
||||||
} else {
|
} else {
|
||||||
des = fmt.Sprintf("%T", wr.write)
|
des = fmt.Sprintf("%T", wr.write)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", streamID, wr.done != nil, des)
|
return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeQueue is used by implementations of WriteScheduler.
|
// writeQueue is used by implementations of WriteScheduler.
|
||||||
|
|
|
@ -388,7 +388,15 @@ func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
||||||
} else {
|
} else {
|
||||||
n = ws.nodes[id]
|
n = ws.nodes[id]
|
||||||
if n == nil {
|
if n == nil {
|
||||||
panic("add on non-open stream")
|
// id is an idle or closed stream. wr should not be a HEADERS or
|
||||||
|
// DATA frame. However, wr can be a RST_STREAM. In this case, we
|
||||||
|
// push wr onto the root, rather than creating a new priorityNode,
|
||||||
|
// since RST_STREAM is tiny and the stream's priority is unknown
|
||||||
|
// anyway. See issue #17919.
|
||||||
|
if wr.DataSize() > 0 {
|
||||||
|
panic("add DATA on non-open stream")
|
||||||
|
}
|
||||||
|
n = &ws.root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n.q.push(wr)
|
n.q.push(wr)
|
||||||
|
|
|
@ -752,7 +752,7 @@ func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
|
||||||
and very unlikely to be the fault of this code.
|
and very unlikely to be the fault of this code.
|
||||||
|
|
||||||
The most likely scenario is that some code elsewhere is using
|
The most likely scenario is that some code elsewhere is using
|
||||||
a requestz.Trace after its Finish method is called.
|
a trace.Trace after its Finish method is called.
|
||||||
You can temporarily set the DebugUseAfterFinish var
|
You can temporarily set the DebugUseAfterFinish var
|
||||||
to help discover where that is; do not leave that var set,
|
to help discover where that is; do not leave that var set,
|
||||||
since it makes this package much less efficient.
|
since it makes this package much less efficient.
|
||||||
|
|
|
@ -899,6 +899,7 @@ func Getpgrp() (pid int) {
|
||||||
//sysnb Getppid() (ppid int)
|
//sysnb Getppid() (ppid int)
|
||||||
//sys Getpriority(which int, who int) (prio int, err error)
|
//sys Getpriority(which int, who int) (prio int, err error)
|
||||||
//sysnb Getrusage(who int, rusage *Rusage) (err error)
|
//sysnb Getrusage(who int, rusage *Rusage) (err error)
|
||||||
|
//sysnb Getsid(pid int) (sid int, err error)
|
||||||
//sysnb Gettid() (tid int)
|
//sysnb Gettid() (tid int)
|
||||||
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
|
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
|
||||||
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
|
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
|
||||||
|
@ -911,7 +912,7 @@ func Getpgrp() (pid int) {
|
||||||
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
||||||
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
||||||
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
|
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
|
||||||
//sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64
|
//sysnb prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
|
||||||
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
|
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
|
||||||
//sys read(fd int, p []byte) (n int, err error)
|
//sys read(fd int, p []byte) (n int, err error)
|
||||||
//sys Removexattr(path string, attr string) (err error)
|
//sys Removexattr(path string, attr string) (err error)
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Getsid(pid int) (sid int, err error) {
|
||||||
|
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
|
||||||
|
sid = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func Gettid() (tid int) {
|
func Gettid() (tid int) {
|
||||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||||
tid = int(r0)
|
tid = int(r0)
|
||||||
|
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
|
||||||
|
|
||||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -576,6 +576,9 @@ var logLevelName = map[int64]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
func logf(c *context, level int64, format string, args ...interface{}) {
|
func logf(c *context, level int64, format string, args ...interface{}) {
|
||||||
|
if c == nil {
|
||||||
|
panic("not an App Engine context")
|
||||||
|
}
|
||||||
s := fmt.Sprintf(format, args...)
|
s := fmt.Sprintf(format, args...)
|
||||||
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
|
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
|
||||||
c.addLogLine(&logpb.UserAppLogLine{
|
c.addLogLine(&logpb.UserAppLogLine{
|
||||||
|
|
|
@ -18,6 +18,22 @@ Prerequisites
|
||||||
|
|
||||||
This requires Go 1.5 or later.
|
This requires Go 1.5 or later.
|
||||||
|
|
||||||
|
A note on the version used: significant performance improvements in benchmarks
|
||||||
|
of grpc-go have been seen by upgrading the go version from 1.5 to the latest
|
||||||
|
1.7.1.
|
||||||
|
|
||||||
|
From https://golang.org/doc/install, one way to install the latest version of go is:
|
||||||
|
```
|
||||||
|
$ GO_VERSION=1.7.1
|
||||||
|
$ OS=linux
|
||||||
|
$ ARCH=amd64
|
||||||
|
$ curl -O https://storage.googleapis.com/golang/go${GO_VERSION}.${OS}-${ARCH}.tar.gz
|
||||||
|
$ sudo tar -C /usr/local -xzf go$GO_VERSION.$OS-$ARCH.tar.gz
|
||||||
|
$ # Put go on the PATH, keep the usual installation dir
|
||||||
|
$ sudo ln -s /usr/local/go/bin/go /usr/bin/go
|
||||||
|
$ rm go$GO_VERSION.$OS-$ARCH.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
Constraints
|
Constraints
|
||||||
-----------
|
-----------
|
||||||
The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the [list](http://godoc.org/google.golang.org/grpc?imports), you need a discussion with gRPC-Go authors and consultants.
|
The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the [list](http://godoc.org/google.golang.org/grpc?imports), you need a discussion with gRPC-Go authors and consultants.
|
||||||
|
@ -30,3 +46,12 @@ Status
|
||||||
------
|
------
|
||||||
GA
|
GA
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Compiling error, undefined: grpc.SupportPackageIsVersion
|
||||||
|
|
||||||
|
Please update proto package, gRPC package and rebuild the proto files:
|
||||||
|
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
|
||||||
|
- `go get -u google.golang.org/grpc`
|
||||||
|
- `protoc --go_out=plugins=grpc:. *.proto`
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/trace"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,7 +50,8 @@ import (
|
||||||
// On error, it returns the error and indicates whether the call should be retried.
|
// On error, it returns the error and indicates whether the call should be retried.
|
||||||
//
|
//
|
||||||
// TODO(zhaoq): Check whether the received message sequence is valid.
|
// TODO(zhaoq): Check whether the received message sequence is valid.
|
||||||
func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) (err error) {
|
// TODO ctx is used for stats collection and processing. It is the context passed from the application.
|
||||||
|
func recvResponse(ctx context.Context, dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) (err error) {
|
||||||
// Try to acquire header metadata from the server if there is any.
|
// Try to acquire header metadata from the server if there is any.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,14 +65,25 @@ func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, s
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p := &parser{r: stream}
|
p := &parser{r: stream}
|
||||||
|
var inPayload *stats.InPayload
|
||||||
|
if stats.On() {
|
||||||
|
inPayload = &stats.InPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
if err = recv(p, dopts.codec, stream, dopts.dc, reply, math.MaxInt32); err != nil {
|
if err = recv(p, dopts.codec, stream, dopts.dc, reply, math.MaxInt32, inPayload); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if inPayload != nil && err == io.EOF && stream.StatusCode() == codes.OK {
|
||||||
|
// TODO in the current implementation, inTrailer may be handled before inPayload in some cases.
|
||||||
|
// Fix the order if necessary.
|
||||||
|
stats.Handle(ctx, inPayload)
|
||||||
|
}
|
||||||
c.trailerMD = stream.Trailer()
|
c.trailerMD = stream.Trailer()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -89,15 +102,27 @@ func sendRequest(ctx context.Context, codec Codec, compressor Compressor, callHd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var cbuf *bytes.Buffer
|
var (
|
||||||
|
cbuf *bytes.Buffer
|
||||||
|
outPayload *stats.OutPayload
|
||||||
|
)
|
||||||
if compressor != nil {
|
if compressor != nil {
|
||||||
cbuf = new(bytes.Buffer)
|
cbuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
outBuf, err := encode(codec, args, compressor, cbuf)
|
if stats.On() {
|
||||||
|
outPayload = &stats.OutPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outBuf, err := encode(codec, args, compressor, cbuf, outPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, Errorf(codes.Internal, "grpc: %v", err)
|
return nil, Errorf(codes.Internal, "grpc: %v", err)
|
||||||
}
|
}
|
||||||
err = t.Write(stream, outBuf, opts)
|
err = t.Write(stream, outBuf, opts)
|
||||||
|
if err == nil && outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
stats.Handle(ctx, outPayload)
|
||||||
|
}
|
||||||
// t.NewStream(...) could lead to an early rejection of the RPC (e.g., the service/method
|
// t.NewStream(...) could lead to an early rejection of the RPC (e.g., the service/method
|
||||||
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
||||||
// recvResponse to get the final status.
|
// recvResponse to get the final status.
|
||||||
|
@ -118,7 +143,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
return invoke(ctx, method, args, reply, cc, opts...)
|
return invoke(ctx, method, args, reply, cc, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) {
|
func invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (e error) {
|
||||||
c := defaultCallInfo
|
c := defaultCallInfo
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
if err := o.before(&c); err != nil {
|
if err := o.before(&c); err != nil {
|
||||||
|
@ -140,12 +165,30 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
|
c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
|
||||||
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
|
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if e != nil {
|
||||||
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{e}}, true)
|
||||||
c.traceInfo.tr.SetError()
|
c.traceInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
if stats.On() {
|
||||||
|
begin := &stats.Begin{
|
||||||
|
Client: true,
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
FailFast: c.failFast,
|
||||||
|
}
|
||||||
|
stats.Handle(ctx, begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if stats.On() {
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
EndTime: time.Now(),
|
||||||
|
Error: e,
|
||||||
|
}
|
||||||
|
stats.Handle(ctx, end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
topts := &transport.Options{
|
topts := &transport.Options{
|
||||||
Last: true,
|
Last: true,
|
||||||
Delay: false,
|
Delay: false,
|
||||||
|
@ -205,7 +248,7 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
err = recvResponse(cc.dopts, t, &c, stream, reply)
|
err = recvResponse(ctx, cc.dopts, t, &c, stream, reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
put()
|
put()
|
||||||
|
|
|
@ -199,6 +199,8 @@ func WithTimeout(d time.Duration) DialOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
|
// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
|
||||||
|
// If FailOnNonTempDialError() is set to true, and an error is returned by f, gRPC checks the error's
|
||||||
|
// Temporary() method to decide if it should try to reconnect to the network address.
|
||||||
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
o.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) {
|
o.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
@ -210,6 +212,17 @@ func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FailOnNonTempDialError returns a DialOption that specified if gRPC fails on non-temporary dial errors.
|
||||||
|
// If f is true, and dialer returns a non-temporary error, gRPC will fail the connection to the network
|
||||||
|
// address and won't try to reconnect.
|
||||||
|
// The default value of FailOnNonTempDialError is false.
|
||||||
|
// This is an EXPERIMENTAL API.
|
||||||
|
func FailOnNonTempDialError(f bool) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.copts.FailOnNonTempDialError = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
|
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
|
||||||
func WithUserAgent(s string) DialOption {
|
func WithUserAgent(s string) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
|
|
|
@ -42,11 +42,13 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -255,9 +257,11 @@ func (p *parser) recvMsg(maxMsgSize int) (pf payloadFormat, msg []byte, err erro
|
||||||
|
|
||||||
// encode serializes msg and prepends the message header. If msg is nil, it
|
// encode serializes msg and prepends the message header. If msg is nil, it
|
||||||
// generates the message header of 0 message length.
|
// generates the message header of 0 message length.
|
||||||
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte, error) {
|
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayload *stats.OutPayload) ([]byte, error) {
|
||||||
var b []byte
|
var (
|
||||||
var length uint
|
b []byte
|
||||||
|
length uint
|
||||||
|
)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
var err error
|
var err error
|
||||||
// TODO(zhaoq): optimize to reduce memory alloc and copying.
|
// TODO(zhaoq): optimize to reduce memory alloc and copying.
|
||||||
|
@ -265,6 +269,12 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if outPayload != nil {
|
||||||
|
outPayload.Payload = msg
|
||||||
|
// TODO truncate large payload.
|
||||||
|
outPayload.Data = b
|
||||||
|
outPayload.Length = len(b)
|
||||||
|
}
|
||||||
if cp != nil {
|
if cp != nil {
|
||||||
if err := cp.Do(cbuf, b); err != nil {
|
if err := cp.Do(cbuf, b); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -295,6 +305,10 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte
|
||||||
// Copy encoded msg to buf
|
// Copy encoded msg to buf
|
||||||
copy(buf[5:], b)
|
copy(buf[5:], b)
|
||||||
|
|
||||||
|
if outPayload != nil {
|
||||||
|
outPayload.WireLength = len(buf)
|
||||||
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,11 +325,14 @@ func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}, maxMsgSize int) error {
|
func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}, maxMsgSize int, inPayload *stats.InPayload) error {
|
||||||
pf, d, err := p.recvMsg(maxMsgSize)
|
pf, d, err := p.recvMsg(maxMsgSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.WireLength = len(d)
|
||||||
|
}
|
||||||
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
|
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -333,6 +350,13 @@ func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{
|
||||||
if err := c.Unmarshal(d, m); err != nil {
|
if err := c.Unmarshal(d, m); err != nil {
|
||||||
return Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
|
return Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.RecvTime = time.Now()
|
||||||
|
inPayload.Payload = m
|
||||||
|
// TODO truncate large payload.
|
||||||
|
inPayload.Data = d
|
||||||
|
inPayload.Length = len(d)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,10 +472,10 @@ func convertCode(err error) codes.Code {
|
||||||
return codes.Unknown
|
return codes.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportPackageIsVersion3 is referenced from generated protocol buffer files
|
// SupportPackageIsVersion4 is referenced from generated protocol buffer files
|
||||||
// to assert that that code is compatible with this version of the grpc package.
|
// to assert that that code is compatible with this version of the grpc package.
|
||||||
//
|
//
|
||||||
// This constant may be renamed in the future if a change in the generated code
|
// This constant may be renamed in the future if a change in the generated code
|
||||||
// requires a synchronised update of grpc-go and protoc-gen-go. This constant
|
// requires a synchronised update of grpc-go and protoc-gen-go. This constant
|
||||||
// should not be referenced from any other code.
|
// should not be referenced from any other code.
|
||||||
const SupportPackageIsVersion3 = true
|
const SupportPackageIsVersion4 = true
|
||||||
|
|
|
@ -54,6 +54,8 @@ import (
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/tap"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,6 +112,7 @@ type options struct {
|
||||||
maxMsgSize int
|
maxMsgSize int
|
||||||
unaryInt UnaryServerInterceptor
|
unaryInt UnaryServerInterceptor
|
||||||
streamInt StreamServerInterceptor
|
streamInt StreamServerInterceptor
|
||||||
|
inTapHandle tap.ServerInHandle
|
||||||
maxConcurrentStreams uint32
|
maxConcurrentStreams uint32
|
||||||
useHandlerImpl bool // use http.Handler-based server
|
useHandlerImpl bool // use http.Handler-based server
|
||||||
}
|
}
|
||||||
|
@ -186,6 +189,17 @@ func StreamInterceptor(i StreamServerInterceptor) ServerOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InTapHandle returns a ServerOption that sets the tap handle for all the server
|
||||||
|
// transport to be created. Only one can be installed.
|
||||||
|
func InTapHandle(h tap.ServerInHandle) ServerOption {
|
||||||
|
return func(o *options) {
|
||||||
|
if o.inTapHandle != nil {
|
||||||
|
panic("The tap handle has been set.")
|
||||||
|
}
|
||||||
|
o.inTapHandle = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewServer creates a gRPC server which has no service registered and has not
|
// NewServer creates a gRPC server which has no service registered and has not
|
||||||
// started to accept requests yet.
|
// started to accept requests yet.
|
||||||
func NewServer(opt ...ServerOption) *Server {
|
func NewServer(opt ...ServerOption) *Server {
|
||||||
|
@ -412,17 +426,22 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
|
||||||
if s.opts.useHandlerImpl {
|
if s.opts.useHandlerImpl {
|
||||||
s.serveUsingHandler(conn)
|
s.serveUsingHandler(conn)
|
||||||
} else {
|
} else {
|
||||||
s.serveNewHTTP2Transport(conn, authInfo)
|
s.serveHTTP2Transport(conn, authInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveNewHTTP2Transport sets up a new http/2 transport (using the
|
// serveHTTP2Transport sets up a http/2 transport (using the
|
||||||
// gRPC http2 server transport in transport/http2_server.go) and
|
// gRPC http2 server transport in transport/http2_server.go) and
|
||||||
// serves streams on it.
|
// serves streams on it.
|
||||||
// This is run in its own goroutine (it does network I/O in
|
// This is run in its own goroutine (it does network I/O in
|
||||||
// transport.NewServerTransport).
|
// transport.NewServerTransport).
|
||||||
func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
||||||
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
|
config := &transport.ServerConfig{
|
||||||
|
MaxStreams: s.opts.maxConcurrentStreams,
|
||||||
|
AuthInfo: authInfo,
|
||||||
|
InTapHandle: s.opts.inTapHandle,
|
||||||
|
}
|
||||||
|
st, err := transport.NewServerTransport("http2", c, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
||||||
|
@ -448,6 +467,12 @@ func (s *Server) serveStreams(st transport.ServerTransport) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
s.handleStream(st, stream, s.traceInfo(st, stream))
|
s.handleStream(st, stream, s.traceInfo(st, stream))
|
||||||
}()
|
}()
|
||||||
|
}, func(ctx context.Context, method string) context.Context {
|
||||||
|
if !EnableTracing {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
tr := trace.New("grpc.Recv."+methodFamily(method), method)
|
||||||
|
return trace.NewContext(ctx, tr)
|
||||||
})
|
})
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
@ -497,15 +522,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
|
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
|
||||||
// If tracing is not enabled, it returns nil.
|
// If tracing is not enabled, it returns nil.
|
||||||
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
||||||
if !EnableTracing {
|
tr, ok := trace.FromContext(stream.Context())
|
||||||
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
trInfo = &traceInfo{
|
trInfo = &traceInfo{
|
||||||
tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
|
tr: tr,
|
||||||
}
|
}
|
||||||
trInfo.firstLine.client = false
|
trInfo.firstLine.client = false
|
||||||
trInfo.firstLine.remoteAddr = st.RemoteAddr()
|
trInfo.firstLine.remoteAddr = st.RemoteAddr()
|
||||||
stream.TraceContext(trInfo.tr)
|
|
||||||
if dl, ok := stream.Context().Deadline(); ok {
|
if dl, ok := stream.Context().Deadline(); ok {
|
||||||
trInfo.firstLine.deadline = dl.Sub(time.Now())
|
trInfo.firstLine.deadline = dl.Sub(time.Now())
|
||||||
}
|
}
|
||||||
|
@ -532,11 +559,17 @@ func (s *Server) removeConn(c io.Closer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
|
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
|
||||||
var cbuf *bytes.Buffer
|
var (
|
||||||
|
cbuf *bytes.Buffer
|
||||||
|
outPayload *stats.OutPayload
|
||||||
|
)
|
||||||
if cp != nil {
|
if cp != nil {
|
||||||
cbuf = new(bytes.Buffer)
|
cbuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
p, err := encode(s.opts.codec, msg, cp, cbuf)
|
if stats.On() {
|
||||||
|
outPayload = &stats.OutPayload{}
|
||||||
|
}
|
||||||
|
p, err := encode(s.opts.codec, msg, cp, cbuf, outPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This typically indicates a fatal issue (e.g., memory
|
// This typically indicates a fatal issue (e.g., memory
|
||||||
// corruption or hardware faults) the application program
|
// corruption or hardware faults) the application program
|
||||||
|
@ -547,10 +580,32 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
|
||||||
// the optimal option.
|
// the optimal option.
|
||||||
grpclog.Fatalf("grpc: Server failed to encode response %v", err)
|
grpclog.Fatalf("grpc: Server failed to encode response %v", err)
|
||||||
}
|
}
|
||||||
return t.Write(stream, p, opts)
|
err = t.Write(stream, p, opts)
|
||||||
|
if err == nil && outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
stats.Handle(stream.Context(), outPayload)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
||||||
|
if stats.On() {
|
||||||
|
begin := &stats.Begin{
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
}
|
||||||
|
stats.Handle(stream.Context(), begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if stats.On() {
|
||||||
|
end := &stats.End{
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
stats.Handle(stream.Context(), end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
defer trInfo.tr.Finish()
|
defer trInfo.tr.Finish()
|
||||||
trInfo.firstLine.client = false
|
trInfo.firstLine.client = false
|
||||||
|
@ -579,14 +634,14 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *rpcError:
|
case *rpcError:
|
||||||
if err := t.WriteStatus(stream, err.code, err.desc); err != nil {
|
if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
case transport.ConnectionError:
|
case transport.ConnectionError:
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
case transport.StreamError:
|
case transport.StreamError:
|
||||||
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
|
if e := t.WriteStatus(stream, err.Code, err.Desc); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
|
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
|
||||||
|
@ -597,20 +652,29 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *rpcError:
|
case *rpcError:
|
||||||
if err := t.WriteStatus(stream, err.code, err.desc); err != nil {
|
if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
if e := t.WriteStatus(stream, codes.Internal, err.Error()); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
|
// TODO checkRecvPayload always return RPC error. Add a return here if necessary.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var inPayload *stats.InPayload
|
||||||
|
if stats.On() {
|
||||||
|
inPayload = &stats.InPayload{
|
||||||
|
RecvTime: time.Now(),
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
statusCode := codes.OK
|
statusCode := codes.OK
|
||||||
statusDesc := ""
|
statusDesc := ""
|
||||||
df := func(v interface{}) error {
|
df := func(v interface{}) error {
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.WireLength = len(req)
|
||||||
|
}
|
||||||
if pf == compressionMade {
|
if pf == compressionMade {
|
||||||
var err error
|
var err error
|
||||||
req, err = s.opts.dc.Do(bytes.NewReader(req))
|
req, err = s.opts.dc.Do(bytes.NewReader(req))
|
||||||
|
@ -618,7 +682,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
||||||
}
|
}
|
||||||
return err
|
return Errorf(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(req) > s.opts.maxMsgSize {
|
if len(req) > s.opts.maxMsgSize {
|
||||||
|
@ -630,6 +694,12 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.Payload = v
|
||||||
|
inPayload.Data = req
|
||||||
|
inPayload.Length = len(req)
|
||||||
|
stats.Handle(stream.Context(), inPayload)
|
||||||
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
|
trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
|
||||||
}
|
}
|
||||||
|
@ -650,9 +720,8 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
|
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return Errorf(statusCode, statusDesc)
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(stringer("OK"), false)
|
trInfo.tr.LazyLog(stringer("OK"), false)
|
||||||
|
@ -677,11 +746,32 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
||||||
}
|
}
|
||||||
return t.WriteStatus(stream, statusCode, statusDesc)
|
errWrite := t.WriteStatus(stream, statusCode, statusDesc)
|
||||||
|
if statusCode != codes.OK {
|
||||||
|
return Errorf(statusCode, statusDesc)
|
||||||
|
}
|
||||||
|
return errWrite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
||||||
|
if stats.On() {
|
||||||
|
begin := &stats.Begin{
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
}
|
||||||
|
stats.Handle(stream.Context(), begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if stats.On() {
|
||||||
|
end := &stats.End{
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
stats.Handle(stream.Context(), end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if s.opts.cp != nil {
|
if s.opts.cp != nil {
|
||||||
stream.SetSendCompress(s.opts.cp.Type())
|
stream.SetSendCompress(s.opts.cp.Type())
|
||||||
}
|
}
|
||||||
|
@ -744,7 +834,11 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
}
|
}
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
return t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
|
errWrite := t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
|
||||||
|
if ss.statusCode != codes.OK {
|
||||||
|
return Errorf(ss.statusCode, ss.statusDesc)
|
||||||
|
}
|
||||||
|
return errWrite
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,7 +853,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil {
|
errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
|
||||||
|
if err := t.WriteStatus(stream, codes.InvalidArgument, errDesc); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
@ -779,7 +874,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil {
|
errDesc := fmt.Sprintf("unknown service %v", service)
|
||||||
|
if err := t.WriteStatus(stream, codes.Unimplemented, errDesc); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
@ -804,7 +900,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil {
|
errDesc := fmt.Sprintf("unknown method %v", method)
|
||||||
|
if err := t.WriteStatus(stream, codes.Unimplemented, errDesc); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package stats is for collecting and reporting various network and RPC stats.
|
||||||
|
// This package is for monitoring purpose only. All fields are read-only.
|
||||||
|
// All APIs are experimental.
|
||||||
|
package stats // import "google.golang.org/grpc/stats"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RPCStats contains stats information about RPCs.
|
||||||
|
// All stats types in this package implements this interface.
|
||||||
|
type RPCStats interface {
|
||||||
|
// IsClient returns true if this RPCStats is from client side.
|
||||||
|
IsClient() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin contains stats when an RPC begins.
|
||||||
|
// FailFast are only valid if Client is true.
|
||||||
|
type Begin struct {
|
||||||
|
// Client is true if this Begin is from client side.
|
||||||
|
Client bool
|
||||||
|
// BeginTime is the time when the RPC begins.
|
||||||
|
BeginTime time.Time
|
||||||
|
// FailFast indicates if this RPC is failfast.
|
||||||
|
FailFast bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *Begin) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// InPayload contains the information for an incoming payload.
|
||||||
|
type InPayload struct {
|
||||||
|
// Client is true if this InPayload is from client side.
|
||||||
|
Client bool
|
||||||
|
// Payload is the payload with original type.
|
||||||
|
Payload interface{}
|
||||||
|
// Data is the serialized message payload.
|
||||||
|
Data []byte
|
||||||
|
// Length is the length of uncompressed data.
|
||||||
|
Length int
|
||||||
|
// WireLength is the length of data on wire (compressed, signed, encrypted).
|
||||||
|
WireLength int
|
||||||
|
// RecvTime is the time when the payload is received.
|
||||||
|
RecvTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *InPayload) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// InHeader contains stats when a header is received.
|
||||||
|
// FullMethod, addresses and Compression are only valid if Client is false.
|
||||||
|
type InHeader struct {
|
||||||
|
// Client is true if this InHeader is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of header.
|
||||||
|
WireLength int
|
||||||
|
|
||||||
|
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||||
|
FullMethod string
|
||||||
|
// RemoteAddr is the remote address of the corresponding connection.
|
||||||
|
RemoteAddr net.Addr
|
||||||
|
// LocalAddr is the local address of the corresponding connection.
|
||||||
|
LocalAddr net.Addr
|
||||||
|
// Compression is the compression algorithm used for the RPC.
|
||||||
|
Compression string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *InHeader) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// InTrailer contains stats when a trailer is received.
|
||||||
|
type InTrailer struct {
|
||||||
|
// Client is true if this InTrailer is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of trailer.
|
||||||
|
WireLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *InTrailer) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// OutPayload contains the information for an outgoing payload.
|
||||||
|
type OutPayload struct {
|
||||||
|
// Client is true if this OutPayload is from client side.
|
||||||
|
Client bool
|
||||||
|
// Payload is the payload with original type.
|
||||||
|
Payload interface{}
|
||||||
|
// Data is the serialized message payload.
|
||||||
|
Data []byte
|
||||||
|
// Length is the length of uncompressed data.
|
||||||
|
Length int
|
||||||
|
// WireLength is the length of data on wire (compressed, signed, encrypted).
|
||||||
|
WireLength int
|
||||||
|
// SentTime is the time when the payload is sent.
|
||||||
|
SentTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *OutPayload) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// OutHeader contains stats when a header is sent.
|
||||||
|
// FullMethod, addresses and Compression are only valid if Client is true.
|
||||||
|
type OutHeader struct {
|
||||||
|
// Client is true if this OutHeader is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of header.
|
||||||
|
WireLength int
|
||||||
|
|
||||||
|
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||||
|
FullMethod string
|
||||||
|
// RemoteAddr is the remote address of the corresponding connection.
|
||||||
|
RemoteAddr net.Addr
|
||||||
|
// LocalAddr is the local address of the corresponding connection.
|
||||||
|
LocalAddr net.Addr
|
||||||
|
// Compression is the compression algorithm used for the RPC.
|
||||||
|
Compression string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *OutHeader) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// OutTrailer contains stats when a trailer is sent.
|
||||||
|
type OutTrailer struct {
|
||||||
|
// Client is true if this OutTrailer is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of trailer.
|
||||||
|
WireLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *OutTrailer) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
// End contains stats when an RPC ends.
|
||||||
|
type End struct {
|
||||||
|
// Client is true if this End is from client side.
|
||||||
|
Client bool
|
||||||
|
// EndTime is the time when the RPC ends.
|
||||||
|
EndTime time.Time
|
||||||
|
// Error is the error just happened. Its type is gRPC error.
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *End) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
var (
|
||||||
|
on = new(int32)
|
||||||
|
handler func(context.Context, RPCStats)
|
||||||
|
)
|
||||||
|
|
||||||
|
// On indicates whether stats is started.
|
||||||
|
func On() bool {
|
||||||
|
return atomic.CompareAndSwapInt32(on, 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle processes the stats using the call back function registered by user.
|
||||||
|
func Handle(ctx context.Context, s RPCStats) {
|
||||||
|
handler(ctx, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterHandler registers the user handler function.
|
||||||
|
// If another handler was registered before, this new handler will overwrite the old one.
|
||||||
|
// This handler function will be called to process the stats.
|
||||||
|
func RegisterHandler(f func(context.Context, RPCStats)) {
|
||||||
|
handler = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the stats collection and reporting if there is a registered stats handle.
|
||||||
|
func Start() {
|
||||||
|
if handler == nil {
|
||||||
|
grpclog.Println("handler is nil when starting stats. Stats is not started")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
atomic.StoreInt32(on, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the stats collection and processing.
|
||||||
|
// Stop does not unregister handler.
|
||||||
|
func Stop() {
|
||||||
|
atomic.StoreInt32(on, 0)
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ import (
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/trace"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ type ClientStream interface {
|
||||||
|
|
||||||
// NewClientStream creates a new Stream for the client side. This is called
|
// NewClientStream creates a new Stream for the client side. This is called
|
||||||
// by generated code.
|
// by generated code.
|
||||||
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) {
|
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
|
||||||
if cc.dopts.streamInt != nil {
|
if cc.dopts.streamInt != nil {
|
||||||
return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...)
|
return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...)
|
||||||
}
|
}
|
||||||
|
@ -143,6 +144,24 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
if stats.On() {
|
||||||
|
begin := &stats.Begin{
|
||||||
|
Client: true,
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
FailFast: c.failFast,
|
||||||
|
}
|
||||||
|
stats.Handle(ctx, begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && stats.On() {
|
||||||
|
// Only handle end stats if err != nil.
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
stats.Handle(ctx, end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
gopts := BalancerGetOptions{
|
gopts := BalancerGetOptions{
|
||||||
BlockingWait: !c.failFast,
|
BlockingWait: !c.failFast,
|
||||||
}
|
}
|
||||||
|
@ -194,6 +213,8 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
|
|
||||||
tracing: EnableTracing,
|
tracing: EnableTracing,
|
||||||
trInfo: trInfo,
|
trInfo: trInfo,
|
||||||
|
|
||||||
|
statsCtx: ctx,
|
||||||
}
|
}
|
||||||
if cc.dopts.cp != nil {
|
if cc.dopts.cp != nil {
|
||||||
cs.cbuf = new(bytes.Buffer)
|
cs.cbuf = new(bytes.Buffer)
|
||||||
|
@ -246,6 +267,11 @@ type clientStream struct {
|
||||||
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
||||||
// and is set to nil when the clientStream's finish method is called.
|
// and is set to nil when the clientStream's finish method is called.
|
||||||
trInfo traceInfo
|
trInfo traceInfo
|
||||||
|
|
||||||
|
// statsCtx keeps the user context for stats handling.
|
||||||
|
// All stats collection should use the statsCtx (instead of the stream context)
|
||||||
|
// so that all the generated stats for a particular RPC can be associated in the processing phase.
|
||||||
|
statsCtx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) Context() context.Context {
|
func (cs *clientStream) Context() context.Context {
|
||||||
|
@ -274,6 +300,8 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
// TODO Investigate how to signal the stats handling party.
|
||||||
|
// generate error stats if err != nil && err != io.EOF?
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cs.finish(err)
|
cs.finish(err)
|
||||||
|
@ -296,7 +324,13 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
err = toRPCErr(err)
|
err = toRPCErr(err)
|
||||||
}()
|
}()
|
||||||
out, err := encode(cs.codec, m, cs.cp, cs.cbuf)
|
var outPayload *stats.OutPayload
|
||||||
|
if stats.On() {
|
||||||
|
outPayload = &stats.OutPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, err := encode(cs.codec, m, cs.cp, cs.cbuf, outPayload)
|
||||||
defer func() {
|
defer func() {
|
||||||
if cs.cbuf != nil {
|
if cs.cbuf != nil {
|
||||||
cs.cbuf.Reset()
|
cs.cbuf.Reset()
|
||||||
|
@ -305,11 +339,37 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Errorf(codes.Internal, "grpc: %v", err)
|
return Errorf(codes.Internal, "grpc: %v", err)
|
||||||
}
|
}
|
||||||
return cs.t.Write(cs.s, out, &transport.Options{Last: false})
|
err = cs.t.Write(cs.s, out, &transport.Options{Last: false})
|
||||||
|
if err == nil && outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
stats.Handle(cs.statsCtx, outPayload)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32)
|
defer func() {
|
||||||
|
if err != nil && stats.On() {
|
||||||
|
// Only generate End if err != nil.
|
||||||
|
// If err == nil, it's not the last RecvMsg.
|
||||||
|
// The last RecvMsg gets either an RPC error or io.EOF.
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
stats.Handle(cs.statsCtx, end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var inPayload *stats.InPayload
|
||||||
|
if stats.On() {
|
||||||
|
inPayload = &stats.InPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32, inPayload)
|
||||||
defer func() {
|
defer func() {
|
||||||
// err != nil indicates the termination of the stream.
|
// err != nil indicates the termination of the stream.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -324,11 +384,15 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
stats.Handle(cs.statsCtx, inPayload)
|
||||||
|
}
|
||||||
if !cs.desc.ClientStreams || cs.desc.ServerStreams {
|
if !cs.desc.ClientStreams || cs.desc.ServerStreams {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Special handling for client streaming rpc.
|
// Special handling for client streaming rpc.
|
||||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32)
|
// This recv expects EOF or errors, so we don't collect inPayload.
|
||||||
|
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32, nil)
|
||||||
cs.closeTransportStream(err)
|
cs.closeTransportStream(err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
|
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
|
||||||
|
@ -482,7 +546,11 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
out, err := encode(ss.codec, m, ss.cp, ss.cbuf)
|
var outPayload *stats.OutPayload
|
||||||
|
if stats.On() {
|
||||||
|
outPayload = &stats.OutPayload{}
|
||||||
|
}
|
||||||
|
out, err := encode(ss.codec, m, ss.cp, ss.cbuf, outPayload)
|
||||||
defer func() {
|
defer func() {
|
||||||
if ss.cbuf != nil {
|
if ss.cbuf != nil {
|
||||||
ss.cbuf.Reset()
|
ss.cbuf.Reset()
|
||||||
|
@ -495,6 +563,10 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
|
||||||
if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil {
|
if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil {
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
if outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
stats.Handle(ss.s.Context(), outPayload)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +585,11 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxMsgSize); err != nil {
|
var inPayload *stats.InPayload
|
||||||
|
if stats.On() {
|
||||||
|
inPayload = &stats.InPayload{}
|
||||||
|
}
|
||||||
|
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxMsgSize, inPayload); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -522,5 +598,8 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
stats.Handle(ss.s.Context(), inPayload)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package tap defines the function handles which are executed on the transport
|
||||||
|
// layer of gRPC-Go and related information. Everything here is EXPERIMENTAL.
|
||||||
|
package tap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Info defines the relevant information needed by the handles.
|
||||||
|
type Info struct {
|
||||||
|
// FullMethodName is the string of grpc method (in the format of
|
||||||
|
// /package.service/method).
|
||||||
|
FullMethodName string
|
||||||
|
// TODO: More to be added.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerInHandle defines the function which runs when a new stream is created
|
||||||
|
// on the server side. Note that it is executed in the per-connection I/O goroutine(s) instead
|
||||||
|
// of per-RPC goroutine. Therefore, users should NOT have any blocking/time-consuming
|
||||||
|
// work in this handle. Otherwise all the RPCs would slow down.
|
||||||
|
type ServerInHandle func(ctx context.Context, info *Info) (context.Context, error)
|
|
@ -268,7 +268,7 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
|
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) {
|
||||||
// With this transport type there will be exactly 1 stream: this HTTP request.
|
// With this transport type there will be exactly 1 stream: this HTTP request.
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
|
|
@ -51,16 +51,19 @@ import (
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
)
|
)
|
||||||
|
|
||||||
// http2Client implements the ClientTransport interface with HTTP2.
|
// http2Client implements the ClientTransport interface with HTTP2.
|
||||||
type http2Client struct {
|
type http2Client struct {
|
||||||
target string // server name/addr
|
target string // server name/addr
|
||||||
userAgent string
|
userAgent string
|
||||||
md interface{}
|
md interface{}
|
||||||
conn net.Conn // underlying communication channel
|
conn net.Conn // underlying communication channel
|
||||||
authInfo credentials.AuthInfo // auth info about the connection
|
remoteAddr net.Addr
|
||||||
nextID uint32 // the next stream ID to be used
|
localAddr net.Addr
|
||||||
|
authInfo credentials.AuthInfo // auth info about the connection
|
||||||
|
nextID uint32 // the next stream ID to be used
|
||||||
|
|
||||||
// writableChan synchronizes write access to the transport.
|
// writableChan synchronizes write access to the transport.
|
||||||
// A writer acquires the write lock by sending a value on writableChan
|
// A writer acquires the write lock by sending a value on writableChan
|
||||||
|
@ -150,6 +153,9 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
conn, err := dial(ctx, opts.Dialer, addr.Addr)
|
conn, err := dial(ctx, opts.Dialer, addr.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if opts.FailOnNonTempDialError {
|
||||||
|
return nil, connectionErrorf(isTemporary(err), err, "transport: %v", err)
|
||||||
|
}
|
||||||
return nil, connectionErrorf(true, err, "transport: %v", err)
|
return nil, connectionErrorf(true, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
// Any further errors will close the underlying connection
|
// Any further errors will close the underlying connection
|
||||||
|
@ -175,11 +181,13 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
t := &http2Client{
|
t := &http2Client{
|
||||||
target: addr.Addr,
|
target: addr.Addr,
|
||||||
userAgent: ua,
|
userAgent: ua,
|
||||||
md: addr.Metadata,
|
md: addr.Metadata,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
authInfo: authInfo,
|
remoteAddr: conn.RemoteAddr(),
|
||||||
|
localAddr: conn.LocalAddr(),
|
||||||
|
authInfo: authInfo,
|
||||||
// The client initiated stream id is odd starting from 1.
|
// The client initiated stream id is odd starting from 1.
|
||||||
nextID: 1,
|
nextID: 1,
|
||||||
writableChan: make(chan int, 1),
|
writableChan: make(chan int, 1),
|
||||||
|
@ -270,12 +278,13 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
||||||
// streams.
|
// streams.
|
||||||
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
||||||
pr := &peer.Peer{
|
pr := &peer.Peer{
|
||||||
Addr: t.conn.RemoteAddr(),
|
Addr: t.remoteAddr,
|
||||||
}
|
}
|
||||||
// Attach Auth info if there is any.
|
// Attach Auth info if there is any.
|
||||||
if t.authInfo != nil {
|
if t.authInfo != nil {
|
||||||
pr.AuthInfo = t.authInfo
|
pr.AuthInfo = t.authInfo
|
||||||
}
|
}
|
||||||
|
userCtx := ctx
|
||||||
ctx = peer.NewContext(ctx, pr)
|
ctx = peer.NewContext(ctx, pr)
|
||||||
authData := make(map[string]string)
|
authData := make(map[string]string)
|
||||||
for _, c := range t.creds {
|
for _, c := range t.creds {
|
||||||
|
@ -347,6 +356,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
return nil, ErrConnClosing
|
return nil, ErrConnClosing
|
||||||
}
|
}
|
||||||
s := t.newStream(ctx, callHdr)
|
s := t.newStream(ctx, callHdr)
|
||||||
|
s.clientStatsCtx = userCtx
|
||||||
t.activeStreams[s.id] = s
|
t.activeStreams[s.id] = s
|
||||||
|
|
||||||
// This stream is not counted when applySetings(...) initialize t.streamsQuota.
|
// This stream is not counted when applySetings(...) initialize t.streamsQuota.
|
||||||
|
@ -413,6 +423,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
first := true
|
first := true
|
||||||
|
bufLen := t.hBuf.Len()
|
||||||
// Sends the headers in a single batch even when they span multiple frames.
|
// Sends the headers in a single batch even when they span multiple frames.
|
||||||
for !endHeaders {
|
for !endHeaders {
|
||||||
size := t.hBuf.Len()
|
size := t.hBuf.Len()
|
||||||
|
@ -447,6 +458,17 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
return nil, connectionErrorf(true, err, "transport: %v", err)
|
return nil, connectionErrorf(true, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if stats.On() {
|
||||||
|
outHeader := &stats.OutHeader{
|
||||||
|
Client: true,
|
||||||
|
WireLength: bufLen,
|
||||||
|
FullMethod: callHdr.Method,
|
||||||
|
RemoteAddr: t.remoteAddr,
|
||||||
|
LocalAddr: t.localAddr,
|
||||||
|
Compression: callHdr.SendCompress,
|
||||||
|
}
|
||||||
|
stats.Handle(s.clientStatsCtx, outHeader)
|
||||||
|
}
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
@ -874,6 +896,24 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
endStream := frame.StreamEnded()
|
endStream := frame.StreamEnded()
|
||||||
|
var isHeader bool
|
||||||
|
defer func() {
|
||||||
|
if stats.On() {
|
||||||
|
if isHeader {
|
||||||
|
inHeader := &stats.InHeader{
|
||||||
|
Client: true,
|
||||||
|
WireLength: int(frame.Header().Length),
|
||||||
|
}
|
||||||
|
stats.Handle(s.clientStatsCtx, inHeader)
|
||||||
|
} else {
|
||||||
|
inTrailer := &stats.InTrailer{
|
||||||
|
Client: true,
|
||||||
|
WireLength: int(frame.Header().Length),
|
||||||
|
}
|
||||||
|
stats.Handle(s.clientStatsCtx, inTrailer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if !endStream {
|
if !endStream {
|
||||||
|
@ -885,6 +925,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
}
|
}
|
||||||
close(s.headerChan)
|
close(s.headerChan)
|
||||||
s.headerDone = true
|
s.headerDone = true
|
||||||
|
isHeader = true
|
||||||
}
|
}
|
||||||
if !endStream || s.state == streamDone {
|
if !endStream || s.state == streamDone {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
|
@ -50,6 +50,8 @@ import (
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrIllegalHeaderWrite indicates that setting header is illegal because of
|
// ErrIllegalHeaderWrite indicates that setting header is illegal because of
|
||||||
|
@ -59,8 +61,11 @@ var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHe
|
||||||
// http2Server implements the ServerTransport interface with HTTP2.
|
// http2Server implements the ServerTransport interface with HTTP2.
|
||||||
type http2Server struct {
|
type http2Server struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
|
remoteAddr net.Addr
|
||||||
|
localAddr net.Addr
|
||||||
maxStreamID uint32 // max stream ID ever seen
|
maxStreamID uint32 // max stream ID ever seen
|
||||||
authInfo credentials.AuthInfo // auth info about the connection
|
authInfo credentials.AuthInfo // auth info about the connection
|
||||||
|
inTapHandle tap.ServerInHandle
|
||||||
// writableChan synchronizes write access to the transport.
|
// writableChan synchronizes write access to the transport.
|
||||||
// A writer acquires the write lock by receiving a value on writableChan
|
// A writer acquires the write lock by receiving a value on writableChan
|
||||||
// and releases it by sending on writableChan.
|
// and releases it by sending on writableChan.
|
||||||
|
@ -91,12 +96,13 @@ type http2Server struct {
|
||||||
|
|
||||||
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
||||||
// returned if something goes wrong.
|
// returned if something goes wrong.
|
||||||
func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (_ ServerTransport, err error) {
|
func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
|
||||||
framer := newFramer(conn)
|
framer := newFramer(conn)
|
||||||
// Send initial settings as connection preface to client.
|
// Send initial settings as connection preface to client.
|
||||||
var settings []http2.Setting
|
var settings []http2.Setting
|
||||||
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
||||||
// permitted in the HTTP2 spec.
|
// permitted in the HTTP2 spec.
|
||||||
|
maxStreams := config.MaxStreams
|
||||||
if maxStreams == 0 {
|
if maxStreams == 0 {
|
||||||
maxStreams = math.MaxUint32
|
maxStreams = math.MaxUint32
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,11 +128,14 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
t := &http2Server{
|
t := &http2Server{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
authInfo: authInfo,
|
remoteAddr: conn.RemoteAddr(),
|
||||||
|
localAddr: conn.LocalAddr(),
|
||||||
|
authInfo: config.AuthInfo,
|
||||||
framer: framer,
|
framer: framer,
|
||||||
hBuf: &buf,
|
hBuf: &buf,
|
||||||
hEnc: hpack.NewEncoder(&buf),
|
hEnc: hpack.NewEncoder(&buf),
|
||||||
maxStreams: maxStreams,
|
maxStreams: maxStreams,
|
||||||
|
inTapHandle: config.InTapHandle,
|
||||||
controlBuf: newRecvBuffer(),
|
controlBuf: newRecvBuffer(),
|
||||||
fc: &inFlow{limit: initialConnWindowSize},
|
fc: &inFlow{limit: initialConnWindowSize},
|
||||||
sendQuotaPool: newQuotaPool(defaultWindowSize),
|
sendQuotaPool: newQuotaPool(defaultWindowSize),
|
||||||
|
@ -142,7 +151,7 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
|
||||||
}
|
}
|
||||||
|
|
||||||
// operateHeader takes action on the decoded headers.
|
// operateHeader takes action on the decoded headers.
|
||||||
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) (close bool) {
|
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (close bool) {
|
||||||
buf := newRecvBuffer()
|
buf := newRecvBuffer()
|
||||||
s := &Stream{
|
s := &Stream{
|
||||||
id: frame.Header().StreamID,
|
id: frame.Header().StreamID,
|
||||||
|
@ -173,7 +182,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
s.ctx, s.cancel = context.WithCancel(context.TODO())
|
s.ctx, s.cancel = context.WithCancel(context.TODO())
|
||||||
}
|
}
|
||||||
pr := &peer.Peer{
|
pr := &peer.Peer{
|
||||||
Addr: t.conn.RemoteAddr(),
|
Addr: t.remoteAddr,
|
||||||
}
|
}
|
||||||
// Attach Auth info if there is any.
|
// Attach Auth info if there is any.
|
||||||
if t.authInfo != nil {
|
if t.authInfo != nil {
|
||||||
|
@ -195,6 +204,18 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
}
|
}
|
||||||
s.recvCompress = state.encoding
|
s.recvCompress = state.encoding
|
||||||
s.method = state.method
|
s.method = state.method
|
||||||
|
if t.inTapHandle != nil {
|
||||||
|
var err error
|
||||||
|
info := &tap.Info{
|
||||||
|
FullMethodName: state.method,
|
||||||
|
}
|
||||||
|
s.ctx, err = t.inTapHandle(s.ctx, info)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Log the real error.
|
||||||
|
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.state != reachable {
|
if t.state != reachable {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
@ -218,13 +239,25 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
s.windowHandler = func(n int) {
|
s.windowHandler = func(n int) {
|
||||||
t.updateWindow(s, uint32(n))
|
t.updateWindow(s, uint32(n))
|
||||||
}
|
}
|
||||||
|
s.ctx = traceCtx(s.ctx, s.method)
|
||||||
|
if stats.On() {
|
||||||
|
inHeader := &stats.InHeader{
|
||||||
|
FullMethod: s.method,
|
||||||
|
RemoteAddr: t.remoteAddr,
|
||||||
|
LocalAddr: t.localAddr,
|
||||||
|
Compression: s.recvCompress,
|
||||||
|
WireLength: int(frame.Header().Length),
|
||||||
|
}
|
||||||
|
stats.Handle(s.ctx, inHeader)
|
||||||
|
}
|
||||||
handle(s)
|
handle(s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleStreams receives incoming streams using the given handler. This is
|
// HandleStreams receives incoming streams using the given handler. This is
|
||||||
// typically run in a separate goroutine.
|
// typically run in a separate goroutine.
|
||||||
func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
// traceCtx attaches trace to ctx and returns the new context.
|
||||||
|
func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) {
|
||||||
// Check the validity of client preface.
|
// Check the validity of client preface.
|
||||||
preface := make([]byte, len(clientPreface))
|
preface := make([]byte, len(clientPreface))
|
||||||
if _, err := io.ReadFull(t.conn, preface); err != nil {
|
if _, err := io.ReadFull(t.conn, preface); err != nil {
|
||||||
|
@ -279,7 +312,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
||||||
}
|
}
|
||||||
switch frame := frame.(type) {
|
switch frame := frame.(type) {
|
||||||
case *http2.MetaHeadersFrame:
|
case *http2.MetaHeadersFrame:
|
||||||
if t.operateHeaders(frame, handle) {
|
if t.operateHeaders(frame, handle, traceCtx) {
|
||||||
t.Close()
|
t.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -492,9 +525,16 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bufLen := t.hBuf.Len()
|
||||||
if err := t.writeHeaders(s, t.hBuf, false); err != nil {
|
if err := t.writeHeaders(s, t.hBuf, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if stats.On() {
|
||||||
|
outHeader := &stats.OutHeader{
|
||||||
|
WireLength: bufLen,
|
||||||
|
}
|
||||||
|
stats.Handle(s.Context(), outHeader)
|
||||||
|
}
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -547,10 +587,17 @@ func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc s
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bufLen := t.hBuf.Len()
|
||||||
if err := t.writeHeaders(s, t.hBuf, true); err != nil {
|
if err := t.writeHeaders(s, t.hBuf, true); err != nil {
|
||||||
t.Close()
|
t.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if stats.On() {
|
||||||
|
outTrailer := &stats.OutTrailer{
|
||||||
|
WireLength: bufLen,
|
||||||
|
}
|
||||||
|
stats.Handle(s.Context(), outTrailer)
|
||||||
|
}
|
||||||
t.closeStream(s)
|
t.closeStream(s)
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return nil
|
return nil
|
||||||
|
@ -767,7 +814,7 @@ func (t *http2Server) closeStream(s *Stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Server) RemoteAddr() net.Addr {
|
func (t *http2Server) RemoteAddr() net.Addr {
|
||||||
return t.conn.RemoteAddr()
|
return t.remoteAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Server) Drain() {
|
func (t *http2Server) Drain() {
|
||||||
|
|
|
@ -45,10 +45,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/trace"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// recvMsg represents the received msg from the transport. All transport
|
// recvMsg represents the received msg from the transport. All transport
|
||||||
|
@ -167,6 +167,11 @@ type Stream struct {
|
||||||
id uint32
|
id uint32
|
||||||
// nil for client side Stream.
|
// nil for client side Stream.
|
||||||
st ServerTransport
|
st ServerTransport
|
||||||
|
// clientStatsCtx keeps the user context for stats handling.
|
||||||
|
// It's only valid on client side. Server side stats context is same as s.ctx.
|
||||||
|
// All client side stats collection should use the clientStatsCtx (instead of the stream context)
|
||||||
|
// so that all the generated stats for a particular RPC can be associated in the processing phase.
|
||||||
|
clientStatsCtx context.Context
|
||||||
// ctx is the associated context of the stream.
|
// ctx is the associated context of the stream.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
// cancel is always nil for client side Stream.
|
// cancel is always nil for client side Stream.
|
||||||
|
@ -266,11 +271,6 @@ func (s *Stream) Context() context.Context {
|
||||||
return s.ctx
|
return s.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceContext recreates the context of s with a trace.Trace.
|
|
||||||
func (s *Stream) TraceContext(tr trace.Trace) {
|
|
||||||
s.ctx = trace.NewContext(s.ctx, tr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method returns the method for the stream.
|
// Method returns the method for the stream.
|
||||||
func (s *Stream) Method() string {
|
func (s *Stream) Method() string {
|
||||||
return s.method
|
return s.method
|
||||||
|
@ -355,10 +355,17 @@ const (
|
||||||
draining
|
draining
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServerConfig consists of all the configurations to establish a server transport.
|
||||||
|
type ServerConfig struct {
|
||||||
|
MaxStreams uint32
|
||||||
|
AuthInfo credentials.AuthInfo
|
||||||
|
InTapHandle tap.ServerInHandle
|
||||||
|
}
|
||||||
|
|
||||||
// NewServerTransport creates a ServerTransport with conn or non-nil error
|
// NewServerTransport creates a ServerTransport with conn or non-nil error
|
||||||
// if it fails.
|
// if it fails.
|
||||||
func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (ServerTransport, error) {
|
func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) {
|
||||||
return newHTTP2Server(conn, maxStreams, authInfo)
|
return newHTTP2Server(conn, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectOptions covers all relevant options for communicating with the server.
|
// ConnectOptions covers all relevant options for communicating with the server.
|
||||||
|
@ -367,6 +374,8 @@ type ConnectOptions struct {
|
||||||
UserAgent string
|
UserAgent string
|
||||||
// Dialer specifies how to dial a network address.
|
// Dialer specifies how to dial a network address.
|
||||||
Dialer func(context.Context, string) (net.Conn, error)
|
Dialer func(context.Context, string) (net.Conn, error)
|
||||||
|
// FailOnNonTempDialError specifies if gRPC fails on non-temporary dial errors.
|
||||||
|
FailOnNonTempDialError bool
|
||||||
// PerRPCCredentials stores the PerRPCCredentials required to issue RPCs.
|
// PerRPCCredentials stores the PerRPCCredentials required to issue RPCs.
|
||||||
PerRPCCredentials []credentials.PerRPCCredentials
|
PerRPCCredentials []credentials.PerRPCCredentials
|
||||||
// TransportCredentials stores the Authenticator required to setup a client connection.
|
// TransportCredentials stores the Authenticator required to setup a client connection.
|
||||||
|
@ -466,7 +475,7 @@ type ClientTransport interface {
|
||||||
// Write methods for a given Stream will be called serially.
|
// Write methods for a given Stream will be called serially.
|
||||||
type ServerTransport interface {
|
type ServerTransport interface {
|
||||||
// HandleStreams receives incoming streams using the given handler.
|
// HandleStreams receives incoming streams using the given handler.
|
||||||
HandleStreams(func(*Stream))
|
HandleStreams(func(*Stream), func(context.Context, string) context.Context)
|
||||||
|
|
||||||
// WriteHeader sends the header metadata for the given stream.
|
// WriteHeader sends the header metadata for the given stream.
|
||||||
// WriteHeader may not be called on all streams.
|
// WriteHeader may not be called on all streams.
|
||||||
|
|
|
@ -19,16 +19,16 @@
|
||||||
"revision": ""
|
"revision": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "M30X+Wqn7AnUr1numUOkQRI7ET0=",
|
"checksumSHA1": "Xt8qcx6rrE1w8HkRSoxJDlNxswA=",
|
||||||
"path": "github.com/Azure/azure-sdk-for-go/storage",
|
"path": "github.com/Azure/azure-sdk-for-go/storage",
|
||||||
"revision": "bd73d950fa4440dae889bd9917bff7cef539f86e",
|
"revision": "27ae5c8b5bc5d90ab0540b4c5d0f2632c8db8b57",
|
||||||
"revisionTime": "2016-10-28T18:31:11Z"
|
"revisionTime": "2016-11-10T23:35:32Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "eWlbQbXextN77vRiVuIBV6kSwuE=",
|
"checksumSHA1": "9BfDgevpBgdiP4B6DT9SvcuVyLs=",
|
||||||
"path": "github.com/Jeffail/gabs",
|
"path": "github.com/Jeffail/gabs",
|
||||||
"revision": "855034b6b7a3b7144977efcaefe72d2c64b0d039",
|
"revision": "2a3aa15961d5fee6047b8151b67ac2f08ba2c48c",
|
||||||
"revisionTime": "2016-08-09T16:55:30Z"
|
"revisionTime": "2016-11-16T21:39:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "LLVyR2dAgkihu0+HdZF+JK0gMMs=",
|
"checksumSHA1": "LLVyR2dAgkihu0+HdZF+JK0gMMs=",
|
||||||
|
@ -43,16 +43,16 @@
|
||||||
"revisionTime": "2015-08-30T18:26:16Z"
|
"revisionTime": "2015-08-30T18:26:16Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "d6798KSc0jDg2MHNxKdgyNfMK7A=",
|
"checksumSHA1": "6HjBxlrYOUnFZo6jPok9ZfQZ/dM=",
|
||||||
"path": "github.com/armon/go-metrics",
|
"path": "github.com/armon/go-metrics",
|
||||||
"revision": "3df31a1ada83e310c2e24b267c8e8b68836547b4",
|
"revision": "97c69685293dce4c0a2d0b19535179bbc976e4d2",
|
||||||
"revisionTime": "2016-07-17T04:34:58Z"
|
"revisionTime": "2016-11-05T01:02:38Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "OmqT9Y1mAHvlAKeJh0jBHC9SH78=",
|
"checksumSHA1": "OmqT9Y1mAHvlAKeJh0jBHC9SH78=",
|
||||||
"path": "github.com/armon/go-metrics/circonus",
|
"path": "github.com/armon/go-metrics/circonus",
|
||||||
"revision": "3df31a1ada83e310c2e24b267c8e8b68836547b4",
|
"revision": "97c69685293dce4c0a2d0b19535179bbc976e4d2",
|
||||||
"revisionTime": "2016-07-17T04:34:58Z"
|
"revisionTime": "2016-11-05T01:02:38Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "gNO0JNpLzYOdInGeq7HqMZUzx9M=",
|
"checksumSHA1": "gNO0JNpLzYOdInGeq7HqMZUzx9M=",
|
||||||
|
@ -67,196 +67,196 @@
|
||||||
"revisionTime": "2016-10-01T16:31:30Z"
|
"revisionTime": "2016-10-01T16:31:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "QEah7lNQcDSLQinY80RVj5CrF3k=",
|
"checksumSHA1": "zCEK2WbnAElGz7x27gMGEyNY19A=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws",
|
"path": "github.com/aws/aws-sdk-go/aws",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=",
|
"checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/awserr",
|
"path": "github.com/aws/aws-sdk-go/aws/awserr",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=",
|
"checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/awsutil",
|
"path": "github.com/aws/aws-sdk-go/aws/awsutil",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=",
|
"checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/client",
|
"path": "github.com/aws/aws-sdk-go/aws/client",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=",
|
"checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
"path": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=",
|
"checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
"path": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=",
|
"checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/credentials",
|
"path": "github.com/aws/aws-sdk-go/aws/credentials",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "KQiUK/zr3mqnAXD7x/X55/iNme0=",
|
"checksumSHA1": "u3GOAJLmdvbuNUeUEcZSEAOeL/0=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
"path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=",
|
"checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
|
"path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=",
|
"checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
|
"path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=",
|
"checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/defaults",
|
"path": "github.com/aws/aws-sdk-go/aws/defaults",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "8E0fEBUJY/1lJOyVxzTxMGQGInk=",
|
"checksumSHA1": "/EXbk/z2TWjWc1Hvb4QYs3Wmhb8=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
"path": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=",
|
"checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/request",
|
"path": "github.com/aws/aws-sdk-go/aws/request",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=",
|
"checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/session",
|
"path": "github.com/aws/aws-sdk-go/aws/session",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "diXvBs1LRC0RJ9WK6sllWKdzC04=",
|
"checksumSHA1": "9K5Bpjvynki7Req1reyD1W3/uWA=",
|
||||||
"path": "github.com/aws/aws-sdk-go/aws/signer/v4",
|
"path": "github.com/aws/aws-sdk-go/aws/signer/v4",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=",
|
"checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/endpoints",
|
"path": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=",
|
"checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol",
|
"path": "github.com/aws/aws-sdk-go/private/protocol",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=",
|
"checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "pNeF0Ey7TfBArH5LBQhKOQXQbLY=",
|
"checksumSHA1": "pNeF0Ey7TfBArH5LBQhKOQXQbLY=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=",
|
"checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=",
|
"checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/query",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/query",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=",
|
"checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "TW/7U+/8ormL7acf6z2rv2hDD+s=",
|
"checksumSHA1": "mLxtfPJvWIHdYPRY0f19kFuJ3u4=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=",
|
"checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/restxml",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/restxml",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=",
|
"checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
"path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=",
|
"checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=",
|
||||||
"path": "github.com/aws/aws-sdk-go/private/waiter",
|
"path": "github.com/aws/aws-sdk-go/private/waiter",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "E5qjR1pDa/V2LEhXP36kZH2w91o=",
|
"checksumSHA1": "E5qjR1pDa/V2LEhXP36kZH2w91o=",
|
||||||
"path": "github.com/aws/aws-sdk-go/service/dynamodb",
|
"path": "github.com/aws/aws-sdk-go/service/dynamodb",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "nNF0ucfWGzZz7PQVe9da0J7Di7w=",
|
"checksumSHA1": "nNF0ucfWGzZz7PQVe9da0J7Di7w=",
|
||||||
"path": "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute",
|
"path": "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=",
|
"checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=",
|
||||||
"path": "github.com/aws/aws-sdk-go/service/ec2",
|
"path": "github.com/aws/aws-sdk-go/service/ec2",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "hQquEMm59E2CwVwfBvKRTVzBj/8=",
|
"checksumSHA1": "hQquEMm59E2CwVwfBvKRTVzBj/8=",
|
||||||
"path": "github.com/aws/aws-sdk-go/service/iam",
|
"path": "github.com/aws/aws-sdk-go/service/iam",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "HtKiIAPKsBg2s1c5ytRkdZ/lqO8=",
|
"checksumSHA1": "HtKiIAPKsBg2s1c5ytRkdZ/lqO8=",
|
||||||
"path": "github.com/aws/aws-sdk-go/service/s3",
|
"path": "github.com/aws/aws-sdk-go/service/s3",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=",
|
"checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=",
|
||||||
"path": "github.com/aws/aws-sdk-go/service/sts",
|
"path": "github.com/aws/aws-sdk-go/service/sts",
|
||||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||||
"revisionTime": "2016-11-01T23:30:21Z"
|
"revisionTime": "2016-11-16T23:00:31Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Isa9x3nvIJ12hvgdvUUBty+yplU=",
|
"checksumSHA1": "Isa9x3nvIJ12hvgdvUUBty+yplU=",
|
||||||
|
@ -291,44 +291,44 @@
|
||||||
{
|
{
|
||||||
"checksumSHA1": "vcncrPAdKKpLjl5O5lAdrFU6NOc=",
|
"checksumSHA1": "vcncrPAdKKpLjl5O5lAdrFU6NOc=",
|
||||||
"path": "github.com/coreos/etcd/client",
|
"path": "github.com/coreos/etcd/client",
|
||||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||||
"revisionTime": "2016-11-02T17:34:52Z"
|
"revisionTime": "2016-11-16T00:44:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ga3jt9r+dQBMSXG0gnpNcXp2xYA=",
|
"checksumSHA1": "ga3jt9r+dQBMSXG0gnpNcXp2xYA=",
|
||||||
"path": "github.com/coreos/etcd/pkg/fileutil",
|
"path": "github.com/coreos/etcd/pkg/fileutil",
|
||||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||||
"revisionTime": "2016-11-02T17:34:52Z"
|
"revisionTime": "2016-11-16T00:44:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "mKIXx1kDwmVmdIpZ3pJtRBuUKso=",
|
"checksumSHA1": "mKIXx1kDwmVmdIpZ3pJtRBuUKso=",
|
||||||
"path": "github.com/coreos/etcd/pkg/pathutil",
|
"path": "github.com/coreos/etcd/pkg/pathutil",
|
||||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||||
"revisionTime": "2016-11-02T17:34:52Z"
|
"revisionTime": "2016-11-16T00:44:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "rMyIh9PsSvPs6Yd+YgKITQzQJx8=",
|
"checksumSHA1": "rMyIh9PsSvPs6Yd+YgKITQzQJx8=",
|
||||||
"path": "github.com/coreos/etcd/pkg/tlsutil",
|
"path": "github.com/coreos/etcd/pkg/tlsutil",
|
||||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||||
"revisionTime": "2016-11-02T17:34:52Z"
|
"revisionTime": "2016-11-16T00:44:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "985rsigkh9SZlDXm6qK6cBloQg0=",
|
"checksumSHA1": "985rsigkh9SZlDXm6qK6cBloQg0=",
|
||||||
"path": "github.com/coreos/etcd/pkg/transport",
|
"path": "github.com/coreos/etcd/pkg/transport",
|
||||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||||
"revisionTime": "2016-11-02T17:34:52Z"
|
"revisionTime": "2016-11-16T00:44:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "gx1gJIMU6T0UNQ0bPZ/drQ8cpCI=",
|
"checksumSHA1": "gx1gJIMU6T0UNQ0bPZ/drQ8cpCI=",
|
||||||
"path": "github.com/coreos/etcd/pkg/types",
|
"path": "github.com/coreos/etcd/pkg/types",
|
||||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||||
"revisionTime": "2016-11-02T17:34:52Z"
|
"revisionTime": "2016-11-16T00:44:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "d50/+u/LFlXvEV10HiEoXB9OsGg=",
|
"checksumSHA1": "d50/+u/LFlXvEV10HiEoXB9OsGg=",
|
||||||
"path": "github.com/coreos/go-systemd/journal",
|
"path": "github.com/coreos/go-systemd/journal",
|
||||||
"revision": "64d5cd7cb947834ef93874e82745c42ad6de4d0e",
|
"revision": "48702e0da86bd25e76cfef347e2adeb434a0d0a6",
|
||||||
"revisionTime": "2016-11-02T17:07:22Z"
|
"revisionTime": "2016-11-14T12:22:54Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "wqEybhAfMhCq2uOu4bT737mqu6U=",
|
"checksumSHA1": "wqEybhAfMhCq2uOu4bT737mqu6U=",
|
||||||
|
@ -391,40 +391,40 @@
|
||||||
"revisionTime": "2016-09-26T17:55:29Z"
|
"revisionTime": "2016-09-26T17:55:29Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "7Di9l3MwdZliLqFKCU9ql2s+Rnw=",
|
"checksumSHA1": "W7OFi8RC+99JY3lVFkqL8dVhUT0=",
|
||||||
"path": "github.com/go-sql-driver/mysql",
|
"path": "github.com/go-sql-driver/mysql",
|
||||||
"revision": "ce924a41eea897745442daaa1739089b0f3f561d",
|
"revision": "665b83488b90b902ce0a305ef6652e599771cdf9",
|
||||||
"revisionTime": "2016-11-01T11:13:14Z"
|
"revisionTime": "2016-11-16T07:07:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Ea5tZxe8OjKyrXAaw5FTrpxiBWw=",
|
"checksumSHA1": "9dGSivFlYCZvic4tBgE0xtWF0x0=",
|
||||||
"path": "github.com/gocql/gocql",
|
"path": "github.com/gocql/gocql",
|
||||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||||
"revisionTime": "2016-11-01T09:30:54Z"
|
"revisionTime": "2016-11-07T21:46:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Z3N6HDGWcvcNu0FloZRq54uO3h4=",
|
"checksumSHA1": "Z3N6HDGWcvcNu0FloZRq54uO3h4=",
|
||||||
"path": "github.com/gocql/gocql/internal/lru",
|
"path": "github.com/gocql/gocql/internal/lru",
|
||||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||||
"revisionTime": "2016-11-01T09:30:54Z"
|
"revisionTime": "2016-11-07T21:46:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ctK9mwZKnt/8dHxx2Ef6nZTljZs=",
|
"checksumSHA1": "ctK9mwZKnt/8dHxx2Ef6nZTljZs=",
|
||||||
"path": "github.com/gocql/gocql/internal/murmur",
|
"path": "github.com/gocql/gocql/internal/murmur",
|
||||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||||
"revisionTime": "2016-11-01T09:30:54Z"
|
"revisionTime": "2016-11-07T21:46:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "tZQDfMMTKrYMXqen0zjJWLtOf1A=",
|
"checksumSHA1": "tZQDfMMTKrYMXqen0zjJWLtOf1A=",
|
||||||
"path": "github.com/gocql/gocql/internal/streams",
|
"path": "github.com/gocql/gocql/internal/streams",
|
||||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||||
"revisionTime": "2016-11-01T09:30:54Z"
|
"revisionTime": "2016-11-07T21:46:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "SVXOQdpDBh0ihdZ5aIflgdA+Rpw=",
|
"checksumSHA1": "QlFGL6EkbK8an+9M+EupFAKe4uM=",
|
||||||
"path": "github.com/golang/protobuf/proto",
|
"path": "github.com/golang/protobuf/proto",
|
||||||
"revision": "98fa357170587e470c5f27d3c3ea0947b71eb455",
|
"revision": "224aaba33b1ac32a92a165f27489409fb8133d08",
|
||||||
"revisionTime": "2016-10-12T20:53:35Z"
|
"revisionTime": "2016-11-16T19:48:24Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "W+E/2xXcE1GmJ0Qb784ald0Fn6I=",
|
"checksumSHA1": "W+E/2xXcE1GmJ0Qb784ald0Fn6I=",
|
||||||
|
@ -433,10 +433,10 @@
|
||||||
"revisionTime": "2016-05-29T05:00:41Z"
|
"revisionTime": "2016-05-29T05:00:41Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "TSS9EqiOoJcVUMNb46zVngVrAO8=",
|
"checksumSHA1": "5+cRIgrrCwJ/BhOG34jUcNyvDPI=",
|
||||||
"path": "github.com/google/go-github/github",
|
"path": "github.com/google/go-github/github",
|
||||||
"revision": "f7fcf6f52ff94adf1cc0ded41e7768d2ad729972",
|
"revision": "d4f1b2d029be1730fd349ca929cc9c0da4a27007",
|
||||||
"revisionTime": "2016-10-28T15:10:40Z"
|
"revisionTime": "2016-11-14T19:06:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "yyAzHoiVLu+xywYI2BDyRq6sOqE=",
|
"checksumSHA1": "yyAzHoiVLu+xywYI2BDyRq6sOqE=",
|
||||||
|
@ -451,16 +451,16 @@
|
||||||
"revisionTime": "2016-01-25T11:53:50Z"
|
"revisionTime": "2016-01-25T11:53:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "LclVLJYrBi03PBjsVPpgoMbUDQ8=",
|
"checksumSHA1": "DHgZ2o3MR8CjlrzkgrjlbyvK/cw=",
|
||||||
"path": "github.com/hashicorp/consul/api",
|
"path": "github.com/hashicorp/consul/api",
|
||||||
"revision": "fb7c03cbb1b3fa71b0746e2f19c442c63e686382",
|
"revision": "74cfcd301485b5dd658715f0282c4e5dff232fca",
|
||||||
"revisionTime": "2016-11-01T13:11:20Z"
|
"revisionTime": "2016-11-16T15:58:24Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "0DPAA2cTBjrCGgXaxXil0vILcFs=",
|
"checksumSHA1": "0DPAA2cTBjrCGgXaxXil0vILcFs=",
|
||||||
"path": "github.com/hashicorp/consul/lib",
|
"path": "github.com/hashicorp/consul/lib",
|
||||||
"revision": "fb7c03cbb1b3fa71b0746e2f19c442c63e686382",
|
"revision": "74cfcd301485b5dd658715f0282c4e5dff232fca",
|
||||||
"revisionTime": "2016-11-01T13:11:20Z"
|
"revisionTime": "2016-11-16T15:58:24Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",
|
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",
|
||||||
|
@ -481,10 +481,10 @@
|
||||||
"revisionTime": "2015-05-18T23:42:57Z"
|
"revisionTime": "2015-05-18T23:42:57Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "KS9lmJV8Z7KHdtSIhbafQLU1hC4=",
|
"checksumSHA1": "jplwF0kQyDhu2Wqq7cmZu0SHjOI=",
|
||||||
"path": "github.com/hashicorp/go-multierror",
|
"path": "github.com/hashicorp/go-multierror",
|
||||||
"revision": "8c5f0ad9360406a3807ce7de6bc73269a91a6e51",
|
"revision": "8484912a3b9987857bac52e0c5fec2b95f419628",
|
||||||
"revisionTime": "2016-08-11T01:57:21Z"
|
"revisionTime": "2016-11-06T17:22:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=",
|
"checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=",
|
||||||
|
@ -525,56 +525,56 @@
|
||||||
{
|
{
|
||||||
"checksumSHA1": "8OPDk+bKyRGJoKcS4QNw9F7dpE8=",
|
"checksumSHA1": "8OPDk+bKyRGJoKcS4QNw9F7dpE8=",
|
||||||
"path": "github.com/hashicorp/hcl",
|
"path": "github.com/hashicorp/hcl",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=",
|
"checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=",
|
||||||
"path": "github.com/hashicorp/hcl/hcl/ast",
|
"path": "github.com/hashicorp/hcl/hcl/ast",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "croNloscHsjX87X+4/cKOURf1EY=",
|
"checksumSHA1": "croNloscHsjX87X+4/cKOURf1EY=",
|
||||||
"path": "github.com/hashicorp/hcl/hcl/parser",
|
"path": "github.com/hashicorp/hcl/hcl/parser",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "lgR7PSAZ0RtvAc9OCtCnNsF/x8g=",
|
"checksumSHA1": "lgR7PSAZ0RtvAc9OCtCnNsF/x8g=",
|
||||||
"path": "github.com/hashicorp/hcl/hcl/scanner",
|
"path": "github.com/hashicorp/hcl/hcl/scanner",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "JlZmnzqdmFFyb1+2afLyR3BOE/8=",
|
"checksumSHA1": "/e0ULfQnGeUKiM1+iMnQhImo62k=",
|
||||||
"path": "github.com/hashicorp/hcl/hcl/strconv",
|
"path": "github.com/hashicorp/hcl/hcl/strconv",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=",
|
"checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=",
|
||||||
"path": "github.com/hashicorp/hcl/hcl/token",
|
"path": "github.com/hashicorp/hcl/hcl/token",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "138aCV5n8n7tkGYMsMVQQnnLq+0=",
|
"checksumSHA1": "138aCV5n8n7tkGYMsMVQQnnLq+0=",
|
||||||
"path": "github.com/hashicorp/hcl/json/parser",
|
"path": "github.com/hashicorp/hcl/json/parser",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "YdvFsNOMSWMLnY6fcliWQa0O5Fw=",
|
"checksumSHA1": "YdvFsNOMSWMLnY6fcliWQa0O5Fw=",
|
||||||
"path": "github.com/hashicorp/hcl/json/scanner",
|
"path": "github.com/hashicorp/hcl/json/scanner",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=",
|
"checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=",
|
||||||
"path": "github.com/hashicorp/hcl/json/token",
|
"path": "github.com/hashicorp/hcl/json/token",
|
||||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||||
"revisionTime": "2016-11-01T18:00:25Z"
|
"revisionTime": "2016-11-12T23:52:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=",
|
"checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=",
|
||||||
|
@ -597,8 +597,8 @@
|
||||||
{
|
{
|
||||||
"checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
|
"checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
|
||||||
"path": "github.com/hashicorp/serf/coordinate",
|
"path": "github.com/hashicorp/serf/coordinate",
|
||||||
"revision": "e0680ca729b018b791af1add413faebe3e1c147e",
|
"revision": "f679d7594a349263f6118db40d87122d3a474e7d",
|
||||||
"revisionTime": "2016-10-31T21:31:47Z"
|
"revisionTime": "2016-11-09T18:57:27Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=",
|
"checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=",
|
||||||
|
@ -673,22 +673,22 @@
|
||||||
"revisionTime": "2016-10-04T15:35:44Z"
|
"revisionTime": "2016-10-04T15:35:44Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "T6yD9DYjn6yO+Kz70bO+sDj20oE=",
|
"checksumSHA1": "avqi4lkviHdrNJ92cXCwrw9x870=",
|
||||||
"path": "github.com/lib/pq",
|
"path": "github.com/lib/pq",
|
||||||
"revision": "a37edb86214894fa6c6c3401a4c4976b02176dd3",
|
"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
|
||||||
"revisionTime": "2016-11-02T07:48:14Z"
|
"revisionTime": "2016-11-03T02:43:54Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=",
|
"checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=",
|
||||||
"path": "github.com/lib/pq/oid",
|
"path": "github.com/lib/pq/oid",
|
||||||
"revision": "a37edb86214894fa6c6c3401a4c4976b02176dd3",
|
"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
|
||||||
"revisionTime": "2016-11-02T07:48:14Z"
|
"revisionTime": "2016-11-03T02:43:54Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "WFbWfoslftbEoKx6ZNVTMcU0EzA=",
|
"checksumSHA1": "I4njd26dG5hxFT2nawuByM4pxzY=",
|
||||||
"path": "github.com/mattn/go-colorable",
|
"path": "github.com/mattn/go-colorable",
|
||||||
"revision": "6e26b354bd2b0fc420cb632b0d878abccdc6544c",
|
"revision": "d228849504861217f796da67fae4f6e347643f15",
|
||||||
"revisionTime": "2016-11-02T08:09:25Z"
|
"revisionTime": "2016-11-03T16:00:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=",
|
"checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=",
|
||||||
|
@ -853,160 +853,172 @@
|
||||||
"revisionTime": "2016-10-31T15:37:30Z"
|
"revisionTime": "2016-10-31T15:37:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "4hQNaJUg67lF/QcO0NKzUeqlaew=",
|
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
|
||||||
"path": "golang.org/x/net/context",
|
"path": "golang.org/x/net/context",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "r+7Ol7uTCa/i5W8pNej9M8xZxWg=",
|
"checksumSHA1": "pLsZUQhI8jm3W9R/4JO9D/L1cUA=",
|
||||||
"path": "golang.org/x/net/http2",
|
"path": "golang.org/x/net/http2",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "HzuGD7AwgC0p1az1WAQnEFnEk98=",
|
"checksumSHA1": "HzuGD7AwgC0p1az1WAQnEFnEk98=",
|
||||||
"path": "golang.org/x/net/http2/hpack",
|
"path": "golang.org/x/net/http2/hpack",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=",
|
"checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=",
|
||||||
"path": "golang.org/x/net/idna",
|
"path": "golang.org/x/net/idna",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=",
|
"checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=",
|
||||||
"path": "golang.org/x/net/internal/timeseries",
|
"path": "golang.org/x/net/internal/timeseries",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=",
|
"checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=",
|
||||||
"path": "golang.org/x/net/lex/httplex",
|
"path": "golang.org/x/net/lex/httplex",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "4MMbG0LI3ghvWooRn36RmDrFIB0=",
|
"checksumSHA1": "P9qTIn8a6L6Q9wd1IJBCuhno1Q8=",
|
||||||
"path": "golang.org/x/net/trace",
|
"path": "golang.org/x/net/trace",
|
||||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||||
"revisionTime": "2016-10-31T16:36:42Z"
|
"revisionTime": "2016-11-15T21:05:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "XH7CgbL5Z8COUc+MKrYqS3FFosY=",
|
"checksumSHA1": "XH7CgbL5Z8COUc+MKrYqS3FFosY=",
|
||||||
"path": "golang.org/x/oauth2",
|
"path": "golang.org/x/oauth2",
|
||||||
"revision": "25b4fb1468cb89700c7c060cb99f30581a61f5e3",
|
"revision": "d5040cddfc0da40b408c9a1da4728662435176a9",
|
||||||
"revisionTime": "2016-10-25T17:59:40Z"
|
"revisionTime": "2016-11-03T22:50:36Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "D3v/aqfB9swlaZcSksCoF+lbOqo=",
|
"checksumSHA1": "D3v/aqfB9swlaZcSksCoF+lbOqo=",
|
||||||
"path": "golang.org/x/oauth2/internal",
|
"path": "golang.org/x/oauth2/internal",
|
||||||
"revision": "25b4fb1468cb89700c7c060cb99f30581a61f5e3",
|
"revision": "d5040cddfc0da40b408c9a1da4728662435176a9",
|
||||||
"revisionTime": "2016-10-25T17:59:40Z"
|
"revisionTime": "2016-11-03T22:50:36Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=",
|
"checksumSHA1": "KqecwXo3OO+p4N+E9RhlHvl9I+w=",
|
||||||
"path": "golang.org/x/sys/unix",
|
"path": "golang.org/x/sys/unix",
|
||||||
"revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
|
"revision": "b699b7032584f0953262cb2788a0ca19bb494703",
|
||||||
"revisionTime": "2016-10-22T18:22:21Z"
|
"revisionTime": "2016-11-10T11:58:56Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "vIZ71Qe81RHec1vNHpKG+CSx/es=",
|
"checksumSHA1": "gYHoPrPncGO926bN0jr1rzDxBQU=",
|
||||||
"path": "google.golang.org/appengine/internal",
|
"path": "google.golang.org/appengine/internal",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=",
|
"checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=",
|
||||||
"path": "google.golang.org/appengine/internal/base",
|
"path": "google.golang.org/appengine/internal/base",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=",
|
"checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=",
|
||||||
"path": "google.golang.org/appengine/internal/datastore",
|
"path": "google.golang.org/appengine/internal/datastore",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=",
|
"checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=",
|
||||||
"path": "google.golang.org/appengine/internal/log",
|
"path": "google.golang.org/appengine/internal/log",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=",
|
"checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=",
|
||||||
"path": "google.golang.org/appengine/internal/remote_api",
|
"path": "google.golang.org/appengine/internal/remote_api",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=",
|
"checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=",
|
||||||
"path": "google.golang.org/appengine/internal/urlfetch",
|
"path": "google.golang.org/appengine/internal/urlfetch",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=",
|
"checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=",
|
||||||
"path": "google.golang.org/appengine/urlfetch",
|
"path": "google.golang.org/appengine/urlfetch",
|
||||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||||
"revisionTime": "2016-10-25T16:43:32Z"
|
"revisionTime": "2016-11-15T22:01:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "r2jmzn/o6RN6PT9FRRtBeAfgNEk=",
|
"checksumSHA1": "xyB2Py2ViSKX8Td+oe2hxG6f0Ak=",
|
||||||
"path": "google.golang.org/grpc",
|
"path": "google.golang.org/grpc",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "08icuA15HRkdYCt6H+Cs90RPQsY=",
|
"checksumSHA1": "08icuA15HRkdYCt6H+Cs90RPQsY=",
|
||||||
"path": "google.golang.org/grpc/codes",
|
"path": "google.golang.org/grpc/codes",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Vd1MU+Ojs7GeS6jE52vlxtXvIrI=",
|
"checksumSHA1": "Vd1MU+Ojs7GeS6jE52vlxtXvIrI=",
|
||||||
"path": "google.golang.org/grpc/credentials",
|
"path": "google.golang.org/grpc/credentials",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "3Lt5hNAG8qJAYSsNghR5uA1zQns=",
|
"checksumSHA1": "3Lt5hNAG8qJAYSsNghR5uA1zQns=",
|
||||||
"path": "google.golang.org/grpc/grpclog",
|
"path": "google.golang.org/grpc/grpclog",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "T3Q0p8kzvXFnRkMaK/G8mCv6mc0=",
|
"checksumSHA1": "T3Q0p8kzvXFnRkMaK/G8mCv6mc0=",
|
||||||
"path": "google.golang.org/grpc/internal",
|
"path": "google.golang.org/grpc/internal",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "P64GkSdsTZ8Nxop5HYqZJ6e+iHs=",
|
"checksumSHA1": "P64GkSdsTZ8Nxop5HYqZJ6e+iHs=",
|
||||||
"path": "google.golang.org/grpc/metadata",
|
"path": "google.golang.org/grpc/metadata",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "4GSUFhOQ0kdFlBH4D5OTeKy78z0=",
|
"checksumSHA1": "4GSUFhOQ0kdFlBH4D5OTeKy78z0=",
|
||||||
"path": "google.golang.org/grpc/naming",
|
"path": "google.golang.org/grpc/naming",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "3RRoLeH6X2//7tVClOVzxW2bY+E=",
|
"checksumSHA1": "3RRoLeH6X2//7tVClOVzxW2bY+E=",
|
||||||
"path": "google.golang.org/grpc/peer",
|
"path": "google.golang.org/grpc/peer",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "HQJrtiTtr5eiRsXQLut2R1Q9kuY=",
|
"checksumSHA1": "FCgy+WB249Vt1XEG5pe4Z7plTLs=",
|
||||||
|
"path": "google.golang.org/grpc/stats",
|
||||||
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "N0TftT6/CyWqp6VRi2DqDx60+Fo=",
|
||||||
|
"path": "google.golang.org/grpc/tap",
|
||||||
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "uVGwQAu6ncFK7qd3BpzbMEJDUP4=",
|
||||||
"path": "google.golang.org/grpc/transport",
|
"path": "google.golang.org/grpc/transport",
|
||||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||||
"revisionTime": "2016-11-02T18:01:57Z"
|
"revisionTime": "2016-11-15T20:54:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=",
|
"checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=",
|
||||||
|
|
Loading…
Reference in New Issue