Vendor and prep for beta

This commit is contained in:
Jeff Mitchell 2019-06-20 23:43:02 -04:00
parent 650cbb297e
commit 6dccc2c6df
278 changed files with 38199 additions and 11857 deletions

2
go.mod
View file

@ -83,7 +83,7 @@ require (
github.com/hashicorp/vault-plugin-secrets-gcpkms v0.5.2-0.20190516000311-88f9a4f11829
github.com/hashicorp/vault-plugin-secrets-kv v0.5.2-0.20190416155133-fd495225dea0
github.com/hashicorp/vault/api v1.0.3-0.20190621032639-b2bd2690d46a
github.com/hashicorp/vault/sdk v0.1.12-0.20190621032639-b2bd2690d46a
github.com/hashicorp/vault/sdk v0.1.12-0.20190621034221-650cbb297e1a
github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
github.com/jackc/pgx v3.3.0+incompatible // indirect

View file

@ -3,7 +3,7 @@
//-------------------------------------------------------------------
variable "download-url" {
default = "https://releases.hashicorp.com/vault/1.1.2/vault_1.1.2_linux_amd64.zip"
default = "https://releases.hashicorp.com/vault/1.2.0-beta1/vault_1.2.0-beta1_linux_amd64.zip"
description = "URL to download Vault"
}

15
vendor/cloud.google.com/go/AUTHORS generated vendored
View file

@ -1,15 +0,0 @@
# This is the official list of cloud authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Filippo Valsorda <hi@filippo.io>
Google Inc.
Ingo Oeser <nightlyone@googlemail.com>
Palm Stone Games, Inc.
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Tyler Treat <ttreat31@gmail.com>

View file

@ -1,40 +0,0 @@
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
# Keep the list alphabetically sorted.
Alexis Hunt <lexer@google.com>
Andreas Litt <andreas.litt@gmail.com>
Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick <bradfitz@golang.org>
Burcu Dogan <jbd@google.com>
Dave Day <djd@golang.org>
David Sansome <me@davidsansome.com>
David Symonds <dsymonds@golang.org>
Filippo Valsorda <hi@filippo.io>
Glenn Lewis <gmlewis@google.com>
Ingo Oeser <nightlyone@googlemail.com>
James Hall <james.hall@shopify.com>
Johan Euphrosine <proppy@google.com>
Jonathan Amsterdam <jba@google.com>
Kunpei Sakai <namusyaka@gmail.com>
Luna Duclos <luna.duclos@palmstonegames.com>
Magnus Hiie <magnus.hiie@gmail.com>
Mario Castro <mariocaster@gmail.com>
Michael McGreevy <mcgreevy@golang.org>
Omar Jarjur <ojarjur@google.com>
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Sarah Adams <shadams@google.com>
Thanatat Tamtan <acoshift@gmail.com>
Toby Burress <kurin@google.com>
Tuo Shan <shantuo@google.com>
Tyler Treat <ttreat31@gmail.com>

View file

@ -94,7 +94,7 @@ type ParseTagFunc func(reflect.StructTag) (name string, keep bool, other interfa
type ValidateFunc func(reflect.Type) error
// LeafTypesFunc is a function that accepts a reflect.Type and returns true if the struct type a leaf, or false if not.
// TODO(deklerk) is this description accurate?
// TODO(deklerk): is this description accurate?
type LeafTypesFunc func(reflect.Type) bool
// A Cache records information about the fields of struct types.

View file

@ -1,61 +0,0 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutil
import (
"math"
"math/big"
"github.com/golang/protobuf/proto"
"github.com/google/go-cmp/cmp"
)
var (
alwaysEqual = cmp.Comparer(func(_, _ interface{}) bool { return true })
defaultCmpOptions = []cmp.Option{
// Use proto.Equal for protobufs
cmp.Comparer(proto.Equal),
// Use big.Rat.Cmp for big.Rats
cmp.Comparer(func(x, y *big.Rat) bool {
if x == nil || y == nil {
return x == y
}
return x.Cmp(y) == 0
}),
// NaNs compare equal
cmp.FilterValues(func(x, y float64) bool {
return math.IsNaN(x) && math.IsNaN(y)
}, alwaysEqual),
cmp.FilterValues(func(x, y float32) bool {
return math.IsNaN(float64(x)) && math.IsNaN(float64(y))
}, alwaysEqual),
}
)
// Equal tests two values for equality.
func Equal(x, y interface{}, opts ...cmp.Option) bool {
// Put default options at the end. Order doesn't matter.
opts = append(opts[:len(opts):len(opts)], defaultCmpOptions...)
return cmp.Equal(x, y, opts...)
}
// Diff reports the differences between two values.
// Diff(x, y) == "" iff Equal(x, y).
func Diff(x, y interface{}, opts ...cmp.Option) string {
// Put default options at the end. Order doesn't matter.
opts = append(opts[:len(opts):len(opts)], defaultCmpOptions...)
return cmp.Diff(x, y, opts...)
}

View file

@ -1,152 +0,0 @@
// Copyright 2014 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package testutil contains helper functions for writing tests.
package testutil
import (
"context"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
)
const (
envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY"
)
// ProjID returns the project ID to use in integration tests, or the empty
// string if none is configured.
func ProjID() string {
return os.Getenv(envProjID)
}
// Credentials returns the credentials to use in integration tests, or nil if
// none is configured. It uses the standard environment variable for tests in
// this repo.
func Credentials(ctx context.Context, scopes ...string) *google.Credentials {
return CredentialsEnv(ctx, envPrivateKey, scopes...)
}
// CredentialsEnv returns the credentials to use in integration tests, or nil
// if none is configured. If the environment variable is unset, CredentialsEnv
// will try to find 'Application Default Credentials'. Else, CredentialsEnv
// will return nil. CredentialsEnv will log.Fatal if the token source is
// specified but missing or invalid.
func CredentialsEnv(ctx context.Context, envVar string, scopes ...string) *google.Credentials {
key := os.Getenv(envVar)
if key == "" { // Try for application default credentials.
creds, err := google.FindDefaultCredentials(ctx, scopes...)
if err != nil {
log.Println("No 'Application Default Credentials' found.")
return nil
}
return creds
}
data, err := ioutil.ReadFile(key)
if err != nil {
log.Fatal(err)
}
creds, err := google.CredentialsFromJSON(ctx, data, scopes...)
if err != nil {
log.Fatal(err)
}
return creds
}
// TokenSource returns the OAuth2 token source to use in integration tests,
// or nil if none is configured. It uses the standard environment variable
// for tests in this repo.
func TokenSource(ctx context.Context, scopes ...string) oauth2.TokenSource {
return TokenSourceEnv(ctx, envPrivateKey, scopes...)
}
// TokenSourceEnv returns the OAuth2 token source to use in integration tests. or nil
// if none is configured. It tries to get credentials from the filename in the
// environment variable envVar. If the environment variable is unset, TokenSourceEnv
// will try to find 'Application Default Credentials'. Else, TokenSourceEnv will
// return nil. TokenSourceEnv will log.Fatal if the token source is specified but
// missing or invalid.
func TokenSourceEnv(ctx context.Context, envVar string, scopes ...string) oauth2.TokenSource {
key := os.Getenv(envVar)
if key == "" { // Try for application default credentials.
ts, err := google.DefaultTokenSource(ctx, scopes...)
if err != nil {
log.Println("No 'Application Default Credentials' found.")
return nil
}
return ts
}
conf, err := jwtConfigFromFile(key, scopes)
if err != nil {
log.Fatal(err)
}
return conf.TokenSource(ctx)
}
// JWTConfig reads the JSON private key file whose name is in the default
// environment variable, and returns the jwt.Config it contains. It ignores
// scopes.
// If the environment variable is empty, it returns (nil, nil).
func JWTConfig() (*jwt.Config, error) {
return jwtConfigFromFile(os.Getenv(envPrivateKey), nil)
}
// jwtConfigFromFile reads the given JSON private key file, and returns the
// jwt.Config it contains.
// If the filename is empty, it returns (nil, nil).
func jwtConfigFromFile(filename string, scopes []string) (*jwt.Config, error) {
if filename == "" {
return nil, nil
}
jsonKey, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("cannot read the JSON key file, err: %v", err)
}
conf, err := google.JWTConfigFromJSON(jsonKey, scopes...)
if err != nil {
return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err)
}
return conf, nil
}
// CanReplay reports whether an integration test can be run in replay mode.
// The replay file must exist, and the GCLOUD_TESTS_GOLANG_ENABLE_REPLAY
// environment variable must be non-empty.
func CanReplay(replayFilename string) bool {
if os.Getenv("GCLOUD_TESTS_GOLANG_ENABLE_REPLAY") == "" {
return false
}
_, err := os.Stat(replayFilename)
return err == nil
}
// ErroringTokenSource is a token source for testing purposes,
// to always return a non-nil error to its caller. It is useful
// when testing error responses with bad oauth2 credentials.
type ErroringTokenSource struct{}
// Token implements oauth2.TokenSource, returning a nil oauth2.Token and a non-nil error.
func (fts ErroringTokenSource) Token() (*oauth2.Token, error) {
return nil, errors.New("intentional error")
}

View file

@ -1,44 +0,0 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutil
import (
"math/rand"
"sync"
"time"
)
// NewRand creates a new *rand.Rand seeded with t. The return value is safe for use
// with multiple goroutines.
func NewRand(t time.Time) *rand.Rand {
s := &lockedSource{src: rand.NewSource(t.UnixNano())}
return rand.New(s)
}
// lockedSource makes a rand.Source safe for use by multiple goroutines.
type lockedSource struct {
mu sync.Mutex
src rand.Source
}
func (ls *lockedSource) Int63() int64 {
ls.mu.Lock()
defer ls.mu.Unlock()
return ls.src.Int63()
}
func (ls *lockedSource) Seed(int64) {
panic("shouldn't be calling Seed")
}

View file

@ -1,135 +0,0 @@
/*
Copyright 2016 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testutil
import (
"fmt"
"log"
"net"
"regexp"
"strconv"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// A Server is an in-process gRPC server, listening on a system-chosen port on
// the local loopback interface. Servers are for testing only and are not
// intended to be used in production code.
//
// To create a server, make a new Server, register your handlers, then call
// Start:
//
// srv, err := NewServer()
// ...
// mypb.RegisterMyServiceServer(srv.Gsrv, &myHandler)
// ....
// srv.Start()
//
// Clients should connect to the server with no security:
//
// conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure())
// ...
type Server struct {
Addr string
Port int
l net.Listener
Gsrv *grpc.Server
}
// NewServer creates a new Server. The Server will be listening for gRPC connections
// at the address named by the Addr field, without TLS.
func NewServer(opts ...grpc.ServerOption) (*Server, error) {
return NewServerWithPort(0, opts...)
}
// NewServerWithPort creates a new Server at a specific port. The Server will be listening
// for gRPC connections at the address named by the Addr field, without TLS.
func NewServerWithPort(port int, opts ...grpc.ServerOption) (*Server, error) {
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return nil, err
}
s := &Server{
Addr: l.Addr().String(),
Port: parsePort(l.Addr().String()),
l: l,
Gsrv: grpc.NewServer(opts...),
}
return s, nil
}
// Start causes the server to start accepting incoming connections.
// Call Start after registering handlers.
func (s *Server) Start() {
go func() {
if err := s.Gsrv.Serve(s.l); err != nil {
log.Printf("testutil.Server.Start: %v", err)
}
}()
}
// Close shuts down the server.
func (s *Server) Close() {
s.Gsrv.Stop()
s.l.Close()
}
// PageBounds converts an incoming page size and token from an RPC request into
// slice bounds and the outgoing next-page token.
//
// PageBounds assumes that the complete, unpaginated list of items exists as a
// single slice. In addition to the page size and token, PageBounds needs the
// length of that slice.
//
// PageBounds's first two return values should be used to construct a sub-slice of
// the complete, unpaginated slice. E.g. if the complete slice is s, then
// s[from:to] is the desired page. Its third return value should be set as the
// NextPageToken field of the RPC response.
func PageBounds(pageSize int, pageToken string, length int) (from, to int, nextPageToken string, err error) {
from, to = 0, length
if pageToken != "" {
from, err = strconv.Atoi(pageToken)
if err != nil {
return 0, 0, "", status.Errorf(codes.InvalidArgument, "bad page token: %v", err)
}
if from >= length {
return length, length, "", nil
}
}
if pageSize > 0 && from+pageSize < length {
to = from + pageSize
nextPageToken = strconv.Itoa(to)
}
return from, to, nextPageToken, nil
}
var portParser = regexp.MustCompile(`:[0-9]+`)
func parsePort(addr string) int {
res := portParser.FindAllString(addr, -1)
if len(res) == 0 {
panic(fmt.Errorf("parsePort: found no numbers in %s", addr))
}
stringPort := res[0][1:] // strip the :
p, err := strconv.ParseInt(stringPort, 10, 32)
if err != nil {
panic(err)
}
return int(p)
}

View file

@ -1,68 +0,0 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutil
import (
"log"
"time"
"go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
)
// TestExporter is a test utility exporter. It should be created with NewtestExporter.
type TestExporter struct {
Spans []*trace.SpanData
Stats chan *view.Data
}
// NewTestExporter creates a TestExporter and registers it with OpenCensus.
func NewTestExporter() *TestExporter {
te := &TestExporter{Stats: make(chan *view.Data)}
view.RegisterExporter(te)
view.SetReportingPeriod(time.Millisecond)
if err := view.Register(ocgrpc.DefaultClientViews...); err != nil {
log.Fatal(err)
}
trace.RegisterExporter(te)
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
return te
}
// ExportSpan exports a span.
func (te *TestExporter) ExportSpan(s *trace.SpanData) {
te.Spans = append(te.Spans, s)
}
// ExportView exports a view.
func (te *TestExporter) ExportView(vd *view.Data) {
if len(vd.Rows) > 0 {
select {
case te.Stats <- vd:
default:
}
}
}
// Unregister unregisters the exporter from OpenCensus.
func (te *TestExporter) Unregister() {
view.UnregisterExporter(te)
trace.UnregisterExporter(te)
view.SetReportingPeriod(0) // reset to default value
}

View file

@ -51,7 +51,7 @@ func toStatus(err error) trace.Status {
}
}
// TODO (deklerk): switch to using OpenCensus function when it becomes available.
// TODO(deklerk): switch to using OpenCensus function when it becomes available.
// Reference: https://github.com/googleapis/googleapis/blob/26b634d2724ac5dd30ae0b0cbfb01f07f2e4050e/google/rpc/code.proto
func httpStatusCodeToOCCode(httpStatusCode int) int32 {
switch httpStatusCode {

View file

@ -97,4 +97,4 @@ func versionGo() string {
return "UNKNOWN"
}
const versionClient = "20190404"
const versionClient = "20190508"

View file

@ -68,7 +68,6 @@ func defaultKeyManagementCallOptions() *KeyManagementCallOptions {
{"default", "idempotent"}: {
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 100 * time.Millisecond,
@ -90,14 +89,14 @@ func defaultKeyManagementCallOptions() *KeyManagementCallOptions {
CreateCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}],
UpdateCryptoKey: retry[[2]string{"default", "non_idempotent"}],
UpdateCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}],
Encrypt: retry[[2]string{"default", "non_idempotent"}],
Decrypt: retry[[2]string{"default", "non_idempotent"}],
Encrypt: retry[[2]string{"default", "idempotent"}],
Decrypt: retry[[2]string{"default", "idempotent"}],
UpdateCryptoKeyPrimaryVersion: retry[[2]string{"default", "non_idempotent"}],
DestroyCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}],
RestoreCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}],
GetPublicKey: retry[[2]string{"default", "idempotent"}],
AsymmetricDecrypt: retry[[2]string{"default", "non_idempotent"}],
AsymmetricSign: retry[[2]string{"default", "non_idempotent"}],
AsymmetricDecrypt: retry[[2]string{"default", "idempotent"}],
AsymmetricSign: retry[[2]string{"default", "idempotent"}],
}
}
@ -203,6 +202,7 @@ func (c *KeyManagementClient) ListKeyRings(ctx context.Context, req *kmspb.ListK
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
it.pageInfo.Token = req.PageToken
return it
}
@ -241,6 +241,7 @@ func (c *KeyManagementClient) ListCryptoKeys(ctx context.Context, req *kmspb.Lis
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
it.pageInfo.Token = req.PageToken
return it
}
@ -279,6 +280,7 @@ func (c *KeyManagementClient) ListCryptoKeyVersions(ctx context.Context, req *km
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
it.pageInfo.Token = req.PageToken
return it
}

View file

@ -108,7 +108,7 @@ func (t *BatchReadOnlyTransaction) PartitionReadUsingIndex(ctx context.Context,
partitions []*Partition
)
kset, err = keys.keySetProto()
// request Partitions
// Request partitions.
if err != nil {
return nil, err
}
@ -121,7 +121,7 @@ func (t *BatchReadOnlyTransaction) PartitionReadUsingIndex(ctx context.Context,
KeySet: kset,
PartitionOptions: opt.toProto(),
})
// prepare ReadRequest
// Prepare ReadRequest.
req := &sppb.ReadRequest{
Session: sid,
Transaction: ts,
@ -130,7 +130,7 @@ func (t *BatchReadOnlyTransaction) PartitionReadUsingIndex(ctx context.Context,
Columns: columns,
KeySet: kset,
}
// generate Partitions
// Generate partitions.
for _, p := range resp.GetPartitions() {
partitions = append(partitions, &Partition{
pt: p.PartitionToken,
@ -140,7 +140,8 @@ func (t *BatchReadOnlyTransaction) PartitionReadUsingIndex(ctx context.Context,
return partitions, err
}
// PartitionQuery returns a list of Partitions that can be used to execute a query against the database.
// PartitionQuery returns a list of Partitions that can be used to execute a
// query against the database.
func (t *BatchReadOnlyTransaction) PartitionQuery(ctx context.Context, statement Statement, opt PartitionOptions) ([]*Partition, error) {
sh, ts, err := t.acquire(ctx)
if err != nil {
@ -188,7 +189,9 @@ func (t *BatchReadOnlyTransaction) release(err error) {
}
// setTimestamp implements txReadEnv.setTimestamp, noop.
// read timestamp is ready on txn initialization, avoid contending writing to it with future partitions.
//
// read timestamp is ready on txn initialization, avoid contending writing to it
// with future partitions.
func (t *BatchReadOnlyTransaction) setTimestamp(ts time.Time) {
}
@ -205,8 +208,8 @@ func (t *BatchReadOnlyTransaction) Close() {
// transaction was shared.
//
// Calling Cleanup is optional, but recommended. If Cleanup is not called, the
// transaction's resources will be freed when the session expires on the backend and
// is deleted. For more information about recycled sessions, see
// transaction's resources will be freed when the session expires on the backend
// and is deleted. For more information about recycled sessions, see
// https://cloud.google.com/spanner/docs/sessions.
func (t *BatchReadOnlyTransaction) Cleanup(ctx context.Context) {
t.Close()
@ -227,7 +230,8 @@ func (t *BatchReadOnlyTransaction) Cleanup(ctx context.Context) {
}
}
// Execute runs a single Partition obtained from PartitionRead or PartitionQuery.
// Execute runs a single Partition obtained from PartitionRead or
// PartitionQuery.
func (t *BatchReadOnlyTransaction) Execute(ctx context.Context, p *Partition) *RowIterator {
var (
sh *sessionHandle
@ -242,7 +246,7 @@ func (t *BatchReadOnlyTransaction) Execute(ctx context.Context, p *Partition) *R
// Might happen if transaction is closed in the middle of a API call.
return &RowIterator{err: errSessionClosed(sh)}
}
// read or query partition
// Read or query partition.
if p.rreq != nil {
p.rreq.PartitionToken = p.pt
rpc = func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) {

View file

@ -39,6 +39,7 @@ const (
// resourcePrefixHeader is the name of the metadata header used to indicate
// the resource being operated on.
resourcePrefixHeader = "google-cloud-resource-prefix"
// xGoogHeaderKey is the name of the metadata header used to indicate client
// information.
xGoogHeaderKey = "x-goog-api-client"
@ -65,13 +66,11 @@ func validDatabaseName(db string) error {
return nil
}
// Client is a client for reading and writing data to a Cloud Spanner database. A
// client is safe to use concurrently, except for its Close method.
// Client is a client for reading and writing data to a Cloud Spanner database.
// A client is safe to use concurrently, except for its Close method.
type Client struct {
// rr must be accessed through atomic operations.
rr uint32
// TODO(deklerk): we should not keep multiple ClientConns / SpannerClients. Instead, we should
// have a single ClientConn that has many connections: https://github.com/googleapis/google-api-go-client/blob/003c13302b3ea5ae44344459ba080364bd46155f/internal/pool.go
rr uint32
conns []*grpc.ClientConn
clients []sppb.SpannerClient
@ -88,10 +87,13 @@ type ClientConfig struct {
// NumChannels is the number of gRPC channels.
// If zero, a reasonable default is used based on the execution environment.
NumChannels int
// SessionPoolConfig is the configuration for session pool.
SessionPoolConfig
// SessionLabels for the sessions created by this client.
// See https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#session for more info.
// See https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#session
// for more info.
SessionLabels map[string]string
}
@ -111,14 +113,14 @@ func contextWithOutgoingMetadata(ctx context.Context, md metadata.MD) context.Co
}
// NewClient creates a client to a database. A valid database name has the
// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID. It uses a default
// configuration.
// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID. It uses
// a default configuration.
func NewClient(ctx context.Context, database string, opts ...option.ClientOption) (*Client, error) {
return NewClientWithConfig(ctx, database, ClientConfig{}, opts...)
}
// NewClientWithConfig creates a client to a database. A valid database name has the
// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID.
// NewClientWithConfig creates a client to a database. A valid database name has
// the form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID.
func NewClientWithConfig(ctx context.Context, database string, config ClientConfig, opts ...option.ClientOption) (c *Client, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/spanner.NewClient")
defer func() { trace.EndSpan(ctx, err) }()
@ -133,12 +135,14 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
resourcePrefixHeader, database,
xGoogHeaderKey, xGoogHeaderVal),
}
// Make a copy of labels.
c.sessionLabels = make(map[string]string)
for k, v := range config.SessionLabels {
c.sessionLabels[k] = v
}
// gRPC options
// gRPC options.
allOpts := []option.ClientOption{
option.WithEndpoint(endpoint),
option.WithScopes(Scope),
@ -150,10 +154,12 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
),
}
allOpts = append(allOpts, opts...)
// Prepare gRPC channels.
if config.NumChannels == 0 {
config.NumChannels = numChannels
}
// Default configs for session pool.
if config.MaxOpened == 0 {
config.MaxOpened = uint64(config.NumChannels * 100)
@ -161,8 +167,10 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
if config.MaxBurst == 0 {
config.MaxBurst = 10
}
// TODO(deklerk) This should be replaced with a balancer with config.NumChannels
// connections, instead of config.NumChannels clientconns.
// TODO(deklerk): This should be replaced with a balancer with
// config.NumChannels connections, instead of config.NumChannels
// clientconns.
for i := 0; i < config.NumChannels; i++ {
conn, err := gtransport.Dial(ctx, allOpts...)
if err != nil {
@ -171,6 +179,7 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
c.conns = append(c.conns, conn)
c.clients = append(c.clients, sppb.NewSpannerClient(conn))
}
// Prepare session pool.
config.SessionPoolConfig.getRPCClient = func() (sppb.SpannerClient, error) {
// TODO: support more loadbalancing options.
@ -186,7 +195,8 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
return c, nil
}
// rrNext returns the next available Cloud Spanner RPC client in a round-robin manner.
// rrNext returns the next available Cloud Spanner RPC client in a round-robin
// manner.
func (c *Client) rrNext() sppb.SpannerClient {
return c.clients[atomic.AddUint32(&c.rr, 1)%uint32(len(c.clients))]
}
@ -259,14 +269,16 @@ func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound
s.delete(ctx)
}
}()
// create session
// Create session.
sc := c.rrNext()
s, err = createSession(ctx, sc, c.database, c.sessionLabels, c.md)
if err != nil {
return nil, err
}
sh = &sessionHandle{session: s}
// begin transaction
// Begin transaction.
err = runRetryable(contextWithOutgoingMetadata(ctx, sh.getMetadata()), func(ctx context.Context) error {
res, e := sh.getClient().BeginTransaction(ctx, &sppb.BeginTransactionRequest{
Session: sh.getID(),
@ -307,7 +319,8 @@ func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound
return t, nil
}
// BatchReadOnlyTransactionFromID reconstruct a BatchReadOnlyTransaction from BatchReadOnlyTransactionID
// BatchReadOnlyTransactionFromID reconstruct a BatchReadOnlyTransaction from
// BatchReadOnlyTransactionID
func (c *Client) BatchReadOnlyTransactionFromID(tid BatchReadOnlyTransactionID) *BatchReadOnlyTransaction {
sc := c.rrNext()
s := &session{valid: true, client: sc, id: tid.sid, createTime: time.Now(), md: c.md}
@ -403,7 +416,8 @@ func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Contex
// applyOption controls the behavior of Client.Apply.
type applyOption struct {
// If atLeastOnce == true, Client.Apply will execute the mutations on Cloud Spanner at least once.
// If atLeastOnce == true, Client.Apply will execute the mutations on Cloud
// Spanner at least once.
atLeastOnce bool
}

View file

@ -1,28 +0,0 @@
/*
Copyright 2018 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package spanner
import (
"cloud.google.com/go/internal/testutil"
"github.com/google/go-cmp/cmp"
)
func testEqual(a, b interface{}) bool {
return testutil.Equal(a, b,
cmp.AllowUnexported(TimestampBound{}, Error{}, Mutation{}, Row{},
Partition{}, BatchReadOnlyTransactionID{}))
}

View file

@ -19,8 +19,8 @@ Package spanner provides a client for reading and writing to Cloud Spanner
databases. See the packages under admin for clients that operate on databases
and instances.
See https://cloud.google.com/spanner/docs/getting-started/go/ for an introduction
to Cloud Spanner and additional help on using this API.
See https://cloud.google.com/spanner/docs/getting-started/go/ for an
introduction to Cloud Spanner and additional help on using this API.
See https://godoc.org/cloud.google.com/go for authentication, timeouts,
connection pooling and similar aspects of this package.
@ -87,8 +87,8 @@ the Kind field to specify other boundary conditions:
KeySets
A KeySet represents a set of keys. A single Key or KeyRange can act as a KeySet. Use
the KeySets function to build the union of several KeySets:
A KeySet represents a set of keys. A single Key or KeyRange can act as a KeySet.
Use the KeySets function to build the union of several KeySets:
ks1 := spanner.KeySets(key1, key2, kr1, kr2)
@ -305,22 +305,51 @@ mutations, which will all be executed at the end of the transaction:
})
Structs
Cloud Spanner STRUCT (aka STRUCT) values
(https://cloud.google.com/spanner/docs/data-types#struct-type) can be
represented by a Go struct value.
A proto StructType is built from the field types and field tag information of
the Go struct. If a field in the struct type definition has a
"spanner:<field_name>" tag, then the value of the "spanner" key in the tag is
used as the name for that field in the built StructType, otherwise the field
name in the struct definition is used. To specify a field with an empty field
name in a Cloud Spanner STRUCT type, use the `spanner:""` tag annotation against
the corresponding field in the Go struct's type definition.
A STRUCT value can contain STRUCT-typed and Array-of-STRUCT typed fields and
these can be specified using named struct-typed and []struct-typed fields inside
a Go struct. However, embedded struct fields are not allowed. Unexported struct
fields are ignored.
NULL STRUCT values in Cloud Spanner are typed. A nil pointer to a Go struct
value can be used to specify a NULL STRUCT value of the corresponding
StructType. Nil and empty slices of a Go STRUCT type can be used to specify
NULL and empty array values respectively of the corresponding StructType. A
slice of pointers to a Go struct type can be used to specify an array of
NULL-able STRUCT values.
DML and Partitioned DML
Spanner supports DML statements like INSERT, UPDATE and DELETE. Use
ReadWriteTransaction.Update to run DML statements. It returns the number of rows
affected. (You can call use ReadWriteTransaction.Query with a DML statement. The first
call to Next on the resulting RowIterator will return iterator.Done, and the RowCount
field of the iterator will hold the number of affected rows.)
affected. (You can call use ReadWriteTransaction.Query with a DML statement. The
first call to Next on the resulting RowIterator will return iterator.Done, and
the RowCount field of the iterator will hold the number of affected rows.)
For large databases, it may be more efficient to partition the DML statement.
Use client.PartitionedUpdate to run a DML statement in this way. Not all DML
statements can be partitioned.
For large databases, it may be more efficient to partition the DML statement. Use
client.PartitionedUpdate to run a DML statement in this way. Not all DML statements
can be partitioned.
Tracing
This client has been instrumented to use OpenCensus tracing (http://opencensus.io).
To enable tracing, see "Enabling Tracing for a Program" at
https://godoc.org/go.opencensus.io/trace. OpenCensus tracing requires Go 1.8 or higher.
This client has been instrumented to use OpenCensus tracing
(http://opencensus.io). To enable tracing, see "Enabling Tracing for a Program"
at https://godoc.org/go.opencensus.io/trace. OpenCensus tracing requires Go 1.8
or higher.
*/
package spanner // import "cloud.google.com/go/spanner"

View file

@ -23,6 +23,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Error is the structured error returned by Cloud Spanner client.
@ -44,6 +45,13 @@ func (e *Error) Error() string {
return fmt.Sprintf("spanner: code = %q, desc = %q", e.Code, e.Desc)
}
// GRPCStatus returns the corresponding gRPC Status of this Spanner error.
// This allows the error to be converted to a gRPC status using
// `status.Convert(error)`.
func (e *Error) GRPCStatus() *status.Status {
return status.New(e.Code, e.Desc)
}
// decorate decorates an existing spanner.Error with more information.
func (e *Error) decorate(info string) {
e.Desc = fmt.Sprintf("%v, %v", info, e.Desc)
@ -63,8 +71,10 @@ func toSpannerError(err error) error {
return toSpannerErrorWithMetadata(err, nil)
}
// toSpannerErrorWithMetadata converts general Go error and grpc trailers to *spanner.Error.
// Note: modifies original error if trailers aren't nil
// toSpannerErrorWithMetadata converts general Go error and grpc trailers to
// *spanner.Error.
//
// Note: modifies original error if trailers aren't nil.
func toSpannerErrorWithMetadata(err error, trailers metadata.MD) error {
if err == nil {
return nil

View file

@ -27,35 +27,36 @@ import (
"google.golang.org/grpc/codes"
)
// A Key can be either a Cloud Spanner row's primary key or a secondary index key.
// It is essentially an interface{} array, which represents a set of Cloud Spanner
// columns. A Key can be used as:
// A Key can be either a Cloud Spanner row's primary key or a secondary index
// key. It is essentially an interface{} array, which represents a set of Cloud
// Spanner columns. A Key can be used as:
//
// - A primary key which uniquely identifies a Cloud Spanner row.
// - A secondary index key which maps to a set of Cloud Spanner rows indexed under it.
// - An endpoint of primary key/secondary index ranges; see the KeyRange type.
//
// Rows that are identified by the Key type are outputs of read operation or targets of
// delete operation in a mutation. Note that for Insert/Update/InsertOrUpdate/Update
// mutation types, although they don't require a primary key explicitly, the column list
// provided must contain enough columns that can comprise a primary key.
// Rows that are identified by the Key type are outputs of read operation or
// targets of delete operation in a mutation. Note that for
// Insert/Update/InsertOrUpdate/Update mutation types, although they don't
// require a primary key explicitly, the column list provided must contain
// enough columns that can comprise a primary key.
//
// Keys are easy to construct. For example, suppose you have a table with a
// primary key of username and product ID. To make a key for this table:
//
// key := spanner.Key{"john", 16}
//
// See the description of Row and Mutation types for how Go types are
// mapped to Cloud Spanner types. For convenience, Key type supports a wide range
// of Go types:
// - int, int8, int16, int32, int64, and NullInt64 are mapped to Cloud Spanner's INT64 type.
// - uint8, uint16 and uint32 are also mapped to Cloud Spanner's INT64 type.
// - float32, float64, NullFloat64 are mapped to Cloud Spanner's FLOAT64 type.
// - bool and NullBool are mapped to Cloud Spanner's BOOL type.
// - []byte is mapped to Cloud Spanner's BYTES type.
// - string and NullString are mapped to Cloud Spanner's STRING type.
// - time.Time and NullTime are mapped to Cloud Spanner's TIMESTAMP type.
// - civil.Date and NullDate are mapped to Cloud Spanner's DATE type.
// See the description of Row and Mutation types for how Go types are mapped to
// Cloud Spanner types. For convenience, Key type supports a wide range of Go
// types:
// - int, int8, int16, int32, int64, and NullInt64 are mapped to Cloud Spanner's INT64 type.
// - uint8, uint16 and uint32 are also mapped to Cloud Spanner's INT64 type.
// - float32, float64, NullFloat64 are mapped to Cloud Spanner's FLOAT64 type.
// - bool and NullBool are mapped to Cloud Spanner's BOOL type.
// - []byte is mapped to Cloud Spanner's BYTES type.
// - string and NullString are mapped to Cloud Spanner's STRING type.
// - time.Time and NullTime are mapped to Cloud Spanner's TIMESTAMP type.
// - civil.Date and NullDate are mapped to Cloud Spanner's DATE type.
type Key []interface{}
// errInvdKeyPartType returns error for unsupported key part type.
@ -344,17 +345,17 @@ func (r KeyRange) keySetProto() (*sppb.KeySet, error) {
return &sppb.KeySet{Ranges: []*sppb.KeyRange{rp}}, nil
}
// A KeySet defines a collection of Cloud Spanner keys and/or key ranges. All the
// keys are expected to be in the same table or index. The keys need not be sorted in
// any particular way.
// A KeySet defines a collection of Cloud Spanner keys and/or key ranges. All
// the keys are expected to be in the same table or index. The keys need not be
// sorted in any particular way.
//
// An individual Key can act as a KeySet, as can a KeyRange. Use the KeySets function
// to create a KeySet consisting of multiple Keys and KeyRanges. To obtain an empty
// KeySet, call KeySets with no arguments.
// An individual Key can act as a KeySet, as can a KeyRange. Use the KeySets
// function to create a KeySet consisting of multiple Keys and KeyRanges. To
// obtain an empty KeySet, call KeySets with no arguments.
//
// If the same key is specified multiple times in the set (for example if two
// ranges, two keys, or a key and a range overlap), the Cloud Spanner backend behaves
// as if the key were only specified once.
// ranges, two keys, or a key and a range overlap), the Cloud Spanner backend
// behaves as if the key were only specified once.
type KeySet interface {
keySetProto() (*sppb.KeySet, error)
}
@ -370,8 +371,8 @@ func (all) keySetProto() (*sppb.KeySet, error) {
return &sppb.KeySet{All: true}, nil
}
// KeySets returns the union of the KeySets. If any of the KeySets is AllKeys, then
// the resulting KeySet will be equivalent to AllKeys.
// KeySets returns the union of the KeySets. If any of the KeySets is AllKeys,
// then the resulting KeySet will be equivalent to AllKeys.
func KeySets(keySets ...KeySet) KeySet {
u := make(union, len(keySets))
copy(u, keySets)

View file

@ -52,8 +52,8 @@ const (
//
// Many mutations can be applied in a single atomic commit. For purposes of
// constraint checking (such as foreign key constraints), the operations can be
// viewed as applying in the same order as the mutations are provided (so that, e.g.,
// a row and its logical "child" can be inserted in the same commit).
// viewed as applying in the same order as the mutations are provided (so that,
// e.g., a row and its logical "child" can be inserted in the same commit).
//
// The Apply function applies series of mutations. For example,
//
@ -66,8 +66,8 @@ const (
// for the new row is UserID (presuming that "user_id" has been declared as the
// primary key of the "User" table).
//
// To apply a series of mutations as part of an atomic read-modify-write operation,
// use ReadWriteTransaction.
// To apply a series of mutations as part of an atomic read-modify-write
// operation, use ReadWriteTransaction.
//
// Updating a row
//
@ -86,8 +86,8 @@ const (
// m := spanner.Delete("User", spanner.Key{UserId})
// _, err := client.Apply(ctx, []*spanner.Mutation{m})
//
// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use the
// spanner.KeySets function to build any combination of Keys and KeyRanges.
// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use
// the spanner.KeySets function to build any combination of Keys and KeyRanges.
//
// Note that deleting a row in a table may also delete rows from other tables
// if cascading deletes are specified in those tables' schemas. Delete does
@ -281,8 +281,9 @@ func InsertOrUpdateMap(table string, in map[string]interface{}) *Mutation {
// Any column values not explicitly written are preserved.
//
// The in argument must be a struct or a pointer to a struct. Its exported
// fields specify the column names and values. Use a field tag like "spanner:name"
// to provide an alternative column name, or use "spanner:-" to ignore the field.
// fields specify the column names and values. Use a field tag like
// "spanner:name" to provide an alternative column name, or use "spanner:-" to
// ignore the field.
//
// For a similar example, See UpdateStruct.
func InsertOrUpdateStruct(table string, in interface{}) (*Mutation, error) {

View file

@ -24,14 +24,14 @@ import (
"google.golang.org/grpc/codes"
)
// PartitionedUpdate executes a DML statement in parallel across the database, using
// separate, internal transactions that commit independently. The DML statement must
// be fully partitionable: it must be expressible as the union of many statements
// each of which accesses only a single row of the table. The statement should also be
// idempotent, because it may be applied more than once.
// PartitionedUpdate executes a DML statement in parallel across the database,
// using separate, internal transactions that commit independently. The DML
// statement must be fully partitionable: it must be expressible as the union
// of many statements each of which accesses only a single row of the table. The
// statement should also be idempotent, because it may be applied more than once.
//
// PartitionedUpdate returns an estimated count of the number of rows affected. The actual
// number of affected rows may be greater than the estimate.
// PartitionedUpdate returns an estimated count of the number of rows affected.
// The actual number of affected rows may be greater than the estimate.
func (c *Client) PartitionedUpdate(ctx context.Context, statement Statement) (count int64, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/spanner.PartitionedUpdate")
defer func() { trace.EndSpan(ctx, err) }()
@ -44,7 +44,7 @@ func (c *Client) PartitionedUpdate(ctx context.Context, statement Statement) (co
s *session
sh *sessionHandle
)
// create session
// Create session.
sc := c.rrNext()
s, err = createSession(ctx, sc, c.database, c.sessionLabels, c.md)
if err != nil {
@ -52,7 +52,7 @@ func (c *Client) PartitionedUpdate(ctx context.Context, statement Statement) (co
}
defer s.delete(ctx)
sh = &sessionHandle{session: s}
// begin transaction
// Begin transaction.
err = runRetryable(contextWithOutgoingMetadata(ctx, sh.getMetadata()), func(ctx context.Context) error {
res, e := sc.BeginTransaction(ctx, &sppb.BeginTransactionRequest{
Session: sh.getID(),

View file

@ -40,13 +40,14 @@ type streamingReceiver interface {
Recv() (*sppb.PartialResultSet, error)
}
// errEarlyReadEnd returns error for read finishes when gRPC stream is still active.
// errEarlyReadEnd returns error for read finishes when gRPC stream is still
// active.
func errEarlyReadEnd() error {
return spannerErrorf(codes.FailedPrecondition, "read completed with active stream")
}
// stream is the internal fault tolerant method for streaming data from
// Cloud Spanner.
// stream is the internal fault tolerant method for streaming data from Cloud
// Spanner.
func stream(ctx context.Context, rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error), setTimestamp func(time.Time), release func(error)) *RowIterator {
ctx, cancel := context.WithCancel(ctx)
ctx = trace.StartSpan(ctx, "cloud.google.com/go/spanner.RowIterator")
@ -61,16 +62,17 @@ func stream(ctx context.Context, rpc func(ct context.Context, resumeToken []byte
// RowIterator is an iterator over Rows.
type RowIterator struct {
// The plan for the query. Available after RowIterator.Next returns iterator.Done
// if QueryWithStats was called.
// The plan for the query. Available after RowIterator.Next returns
// iterator.Done if QueryWithStats was called.
QueryPlan *sppb.QueryPlan
// Execution statistics for the query. Available after RowIterator.Next returns iterator.Done
// if QueryWithStats was called.
// Execution statistics for the query. Available after RowIterator.Next
// returns iterator.Done if QueryWithStats was called.
QueryStats map[string]interface{}
// For a DML statement, the number of rows affected. For PDML, this is a lower bound.
// Available for DML statements after RowIterator.Next returns iterator.Done.
// For a DML statement, the number of rows affected. For PDML, this is a
// lower bound. Available for DML statements after RowIterator.Next returns
// iterator.Done.
RowCount int64
streamd *resumableStreamDecoder
@ -142,8 +144,9 @@ func extractRowCount(stats *sppb.ResultSetStats) (int64, error) {
}
}
// Do calls the provided function once in sequence for each row in the iteration. If the
// function returns a non-nil error, Do immediately returns that error.
// Do calls the provided function once in sequence for each row in the
// iteration. If the function returns a non-nil error, Do immediately returns
// that error.
//
// If there are no rows in the iterator, Do will return nil without calling the
// provided function.
@ -166,7 +169,8 @@ func (r *RowIterator) Do(f func(r *Row) error) error {
}
}
// Stop terminates the iteration. It should be called after you finish using the iterator.
// Stop terminates the iteration. It should be called after you finish using the
// iterator.
func (r *RowIterator) Stop() {
if r.streamd != nil {
defer trace.EndSpan(r.streamd.ctx, r.err)
@ -184,8 +188,8 @@ func (r *RowIterator) Stop() {
}
}
// partialResultQueue implements a simple FIFO queue. The zero value is a
// valid queue.
// partialResultQueue implements a simple FIFO queue. The zero value is a valid
// queue.
type partialResultQueue struct {
q []*sppb.PartialResultSet
first int
@ -231,8 +235,7 @@ func (q *partialResultQueue) push(r *sppb.PartialResultSet) {
q.n++
}
// pop removes an item from the head of partialResultQueue and returns
// it.
// pop removes an item from the head of partialResultQueue and returns it.
func (q *partialResultQueue) pop() *sppb.PartialResultSet {
if q.n == 0 {
return nil
@ -259,8 +262,8 @@ func (q *partialResultQueue) dump() []*sppb.PartialResultSet {
return dq
}
// resumableStreamDecoderState encodes resumableStreamDecoder's status.
// See also the comments for resumableStreamDecoder.Next.
// resumableStreamDecoderState encodes resumableStreamDecoder's status. See also
// the comments for resumableStreamDecoder.Next.
type resumableStreamDecoderState int
const (
@ -278,45 +281,58 @@ type resumableStreamDecoder struct {
// state is the current status of resumableStreamDecoder, see also
// the comments for resumableStreamDecoder.Next.
state resumableStreamDecoderState
// stateWitness when non-nil is called to observe state change,
// used for testing.
stateWitness func(resumableStreamDecoderState)
// ctx is the caller's context, used for cancel/timeout Next().
ctx context.Context
// rpc is a factory of streamingReceiver, which might resume
// a previous stream from the point encoded in restartToken.
// rpc is always a wrapper of a Cloud Spanner query which is
// resumable.
rpc func(ctx context.Context, restartToken []byte) (streamingReceiver, error)
// stream is the current RPC streaming receiver.
stream streamingReceiver
// q buffers received yet undecoded partial results.
q partialResultQueue
// bytesBetweenResumeTokens is the proxy of the byte size of PartialResultSets being queued
// between two resume tokens. Once bytesBetweenResumeTokens is greater than
// maxBytesBetweenResumeTokens, resumableStreamDecoder goes into queueingUnretryable state.
// bytesBetweenResumeTokens is the proxy of the byte size of
// PartialResultSets being queued between two resume tokens. Once
// bytesBetweenResumeTokens is greater than maxBytesBetweenResumeTokens,
// resumableStreamDecoder goes into queueingUnretryable state.
bytesBetweenResumeTokens int32
// maxBytesBetweenResumeTokens is the max number of bytes that can be buffered
// between two resume tokens. It is always copied from the global maxBytesBetweenResumeTokens
// atomically.
// maxBytesBetweenResumeTokens is the max number of bytes that can be
// buffered between two resume tokens. It is always copied from the global
// maxBytesBetweenResumeTokens atomically.
maxBytesBetweenResumeTokens int32
// np is the next sppb.PartialResultSet ready to be returned
// to caller of resumableStreamDecoder.Get().
np *sppb.PartialResultSet
// resumeToken stores the resume token that resumableStreamDecoder has
// last revealed to caller.
resumeToken []byte
// retryCount is the number of retries that have been carried out so far
retryCount int
// err is the last error resumableStreamDecoder has encountered so far.
err error
// backoff to compute delays between retries.
backoff backoff.ExponentialBackoff
}
// newResumableStreamDecoder creates a new resumeableStreamDecoder instance.
// Parameter rpc should be a function that creates a new stream
// beginning at the restartToken if non-nil.
// Parameter rpc should be a function that creates a new stream beginning at the
// restartToken if non-nil.
func newResumableStreamDecoder(ctx context.Context, rpc func(ct context.Context, restartToken []byte) (streamingReceiver, error)) *resumableStreamDecoder {
return &resumableStreamDecoder{
ctx: ctx,
@ -329,8 +345,8 @@ func newResumableStreamDecoder(ctx context.Context, rpc func(ct context.Context,
// changeState fulfills state transition for resumableStateDecoder.
func (d *resumableStreamDecoder) changeState(target resumableStreamDecoderState) {
if d.state == queueingRetryable && d.state != target {
// Reset bytesBetweenResumeTokens because it is only meaningful/changed under
// queueingRetryable state.
// Reset bytesBetweenResumeTokens because it is only meaningful/changed
// under queueingRetryable state.
d.bytesBetweenResumeTokens = 0
}
d.state = target
@ -398,9 +414,9 @@ func (d *resumableStreamDecoder) isNewResumeToken(rt []byte) bool {
*/
var (
// maxBytesBetweenResumeTokens is the maximum amount of bytes that resumableStreamDecoder
// in queueingRetryable state can use to queue PartialResultSets before getting
// into queueingUnretryable state.
// maxBytesBetweenResumeTokens is the maximum amount of bytes that
// resumableStreamDecoder in queueingRetryable state can use to queue
// PartialResultSets before getting into queueingUnretryable state.
maxBytesBetweenResumeTokens = int32(128 * 1024 * 1024)
)
@ -440,17 +456,20 @@ func (d *resumableStreamDecoder) next() bool {
// Receiving queue is not empty.
last, err := d.q.peekLast()
if err != nil {
// Only the case that receiving queue is empty could cause peekLast to
// return error and in such case, we should try to receive from stream.
// Only the case that receiving queue is empty could cause
// peekLast to return error and in such case, we should try to
// receive from stream.
d.tryRecv()
continue
}
if d.isNewResumeToken(last.ResumeToken) {
// Got new resume token, return buffered sppb.PartialResultSets to caller.
// Got new resume token, return buffered sppb.PartialResultSets
// to caller.
d.np = d.q.pop()
if d.q.empty() {
d.bytesBetweenResumeTokens = 0
// The new resume token was just popped out from queue, record it.
// The new resume token was just popped out from queue,
// record it.
d.resumeToken = d.np.ResumeToken
d.changeState(queueingRetryable)
}
@ -461,9 +480,9 @@ func (d *resumableStreamDecoder) next() bool {
continue
}
if d.state == queueingUnretryable {
// When there is no resume token observed,
// only yield sppb.PartialResultSets to caller under
// queueingUnretryable state.
// When there is no resume token observed, only yield
// sppb.PartialResultSets to caller under queueingUnretryable
// state.
d.np = d.q.pop()
return true
}
@ -472,8 +491,8 @@ func (d *resumableStreamDecoder) next() bool {
d.tryRecv()
continue
case aborted:
// Discard all pending items because none of them
// should be yield to caller.
// Discard all pending items because none of them should be yield
// to caller.
d.q.clear()
return false
case finished:
@ -482,7 +501,8 @@ func (d *resumableStreamDecoder) next() bool {
// No buffered PartialResultSet.
return false
}
// Although query has finished, there are still buffered PartialResultSets.
// Although query has finished, there are still buffered
// PartialResultSets.
d.np = d.q.pop()
return true
@ -516,16 +536,14 @@ func (d *resumableStreamDecoder) tryRecv() {
}
d.q.push(res)
if d.state == queueingRetryable && !d.isNewResumeToken(res.ResumeToken) {
// adjusting d.bytesBetweenResumeTokens
d.bytesBetweenResumeTokens += int32(proto.Size(res))
}
d.resetBackOff()
d.changeState(d.state)
}
// resetBackOff clears the internal retry counter of
// resumableStreamDecoder so that the next exponential
// backoff will start at a fresh state.
// resetBackOff clears the internal retry counter of resumableStreamDecoder so
// that the next exponential backoff will start at a fresh state.
func (d *resumableStreamDecoder) resetBackOff() {
d.retryCount = 0
}
@ -558,8 +576,9 @@ func (d *resumableStreamDecoder) lastErr() error {
type partialResultSetDecoder struct {
row Row
tx *sppb.Transaction
chunked bool // if true, next value should be merged with last values entry.
ts time.Time // read timestamp
chunked bool // if true, next value should be merged with last values
// entry.
ts time.Time // read timestamp
}
// yield checks we have a complete row, and if so returns it. A row is not
@ -567,9 +586,9 @@ type partialResultSetDecoder struct {
// and there are no further values to process.
func (p *partialResultSetDecoder) yield(chunked, last bool) *Row {
if len(p.row.vals) == len(p.row.fields) && (!chunked || !last) {
// When partialResultSetDecoder gets enough number of
// Column values, There are two cases that a new Row
// should be yield:
// When partialResultSetDecoder gets enough number of Column values.
// There are two cases that a new Row should be yield:
//
// 1. The incoming PartialResultSet is not chunked;
// 2. The incoming PartialResultSet is chunked, but the
// proto3.Value being merged is not the last one in
@ -594,8 +613,8 @@ func errChunkedEmptyRow() error {
return spannerErrorf(codes.FailedPrecondition, "got invalid chunked PartialResultSet with empty Row")
}
// add tries to merge a new PartialResultSet into buffered Row. It returns
// any rows that have been completed as a result.
// add tries to merge a new PartialResultSet into buffered Row. It returns any
// rows that have been completed as a result.
func (p *partialResultSetDecoder) add(r *sppb.PartialResultSet) ([]*Row, error) {
var rows []*Row
if r.Metadata != nil {
@ -615,14 +634,14 @@ func (p *partialResultSetDecoder) add(r *sppb.PartialResultSet) ([]*Row, error)
}
if p.chunked {
p.chunked = false
// Try to merge first value in r.Values into
// uncompleted row.
// Try to merge first value in r.Values into uncompleted row.
last := len(p.row.vals) - 1
if last < 0 { // sanity check
return nil, errChunkedEmptyRow()
}
var err error
// If p is chunked, then we should always try to merge p.last with r.first.
// If p is chunked, then we should always try to merge p.last with
// r.first.
if p.row.vals[last], err = p.merge(p.row.vals[last], r.Values[0]); err != nil {
return nil, err
}
@ -635,22 +654,22 @@ func (p *partialResultSetDecoder) add(r *sppb.PartialResultSet) ([]*Row, error)
for i, v := range r.Values {
// The rest values in r can be appened into p directly.
p.row.vals = append(p.row.vals, v)
// Again, check to see if a complete Row can be yielded because of
// the newly added value.
// Again, check to see if a complete Row can be yielded because of the
// newly added value.
if row := p.yield(r.ChunkedValue, i == len(r.Values)-1); row != nil {
rows = append(rows, row)
}
}
if r.ChunkedValue {
// After dealing with all values in r, if r is chunked then p must
// be also chunked.
// After dealing with all values in r, if r is chunked then p must be
// also chunked.
p.chunked = true
}
return rows, nil
}
// isMergeable returns if a protobuf Value can be potentially merged with
// other protobuf Values.
// isMergeable returns if a protobuf Value can be potentially merged with other
// protobuf Values.
func (p *partialResultSetDecoder) isMergeable(a *proto3.Value) bool {
switch a.Kind.(type) {
case *proto3.Value_StringValue:
@ -662,14 +681,14 @@ func (p *partialResultSetDecoder) isMergeable(a *proto3.Value) bool {
}
}
// errIncompatibleMergeTypes returns error for incompatible protobuf types
// that cannot be merged by partialResultSetDecoder.
// errIncompatibleMergeTypes returns error for incompatible protobuf types that
// cannot be merged by partialResultSetDecoder.
func errIncompatibleMergeTypes(a, b *proto3.Value) error {
return spannerErrorf(codes.FailedPrecondition, "incompatible type in chunked PartialResultSet. expected (%T), got (%T)", a.Kind, b.Kind)
}
// errUnsupportedMergeType returns error for protobuf type that cannot be
// merged to other protobufs.
// errUnsupportedMergeType returns error for protobuf type that cannot be merged
// to other protobufs.
func errUnsupportedMergeType(a *proto3.Value) error {
return spannerErrorf(codes.FailedPrecondition, "unsupported type merge (%T)", a.Kind)
}
@ -701,9 +720,9 @@ func (p *partialResultSetDecoder) merge(a, b *proto3.Value) (*proto3.Value, erro
return b, nil
}
if la := len(t.ListValue.Values) - 1; p.isMergeable(t.ListValue.Values[la]) {
// When the last item in a is of type String,
// List or Struct(encoded into List by Cloud Spanner),
// try to Merge last item in a and first item in b.
// When the last item in a is of type String, List or Struct
// (encoded into List by Cloud Spanner), try to Merge last item in
// a and first item in b.
t.ListValue.Values[la], err = p.merge(t.ListValue.Values[la], l.ListValue.Values[0])
if err != nil {
return nil, err
@ -726,8 +745,8 @@ func (p *partialResultSetDecoder) merge(a, b *proto3.Value) (*proto3.Value, erro
// Done returns if partialResultSetDecoder has already done with all buffered
// values.
func (p *partialResultSetDecoder) done() bool {
// There is no explicit end of stream marker, but ending part way
// through a row is obviously bad, or ending with the last column still
// awaiting completion.
// There is no explicit end of stream marker, but ending part way through a
// row is obviously bad, or ending with the last column still awaiting
// completion.
return len(p.row.vals) == 0 && !p.chunked
}

View file

@ -45,22 +45,23 @@ func errRetry(err error) error {
return spannerErrorf(codes.Unavailable, "generic Cloud Spanner retryable error: { %v }", err.Error())
}
// isErrorClosing reports whether the error is generated by gRPC layer talking to a closed server.
// isErrorClosing reports whether the error is generated by gRPC layer talking
// to a closed server.
func isErrorClosing(err error) bool {
if err == nil {
return false
}
if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "transport is closing") {
// Handle the case when connection is closed unexpectedly.
// TODO: once gRPC is able to categorize
// this as retryable error, we should stop parsing the
// error message here.
// TODO: once gRPC is able to categorize this as retryable error, we
// should stop parsing the error message here.
return true
}
return false
}
// isErrorRST reports whether the error is generated by gRPC client receiving a RST frame from server.
// isErrorRST reports whether the error is generated by gRPC client receiving a
// RST frame from server.
func isErrorRST(err error) bool {
if err == nil {
return false
@ -73,7 +74,8 @@ func isErrorRST(err error) bool {
return false
}
// isErrorUnexpectedEOF returns true if error is generated by gRPC layer receiving io.EOF unexpectedly.
// isErrorUnexpectedEOF returns true if error is generated by gRPC layer
// receiving io.EOF unexpectedly.
func isErrorUnexpectedEOF(err error) bool {
if err == nil {
return false
@ -81,6 +83,7 @@ func isErrorUnexpectedEOF(err error) bool {
// Unexpected EOF is a transport layer issue that could be recovered by
// retries. The most likely scenario is a flaky RecvMsg() call due to
// network issues.
//
// For grpc version >= 1.14.0, the error code is Internal.
// (https://github.com/grpc/grpc-go/releases/tag/v1.14.0)
if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "unexpected EOF") {
@ -93,7 +96,8 @@ func isErrorUnexpectedEOF(err error) bool {
return false
}
// isErrorUnavailable returns true if the error is about server being unavailable.
// isErrorUnavailable returns true if the error is about server being
// unavailable.
func isErrorUnavailable(err error) bool {
if err == nil {
return false
@ -104,7 +108,8 @@ func isErrorUnavailable(err error) bool {
return false
}
// isRetryable returns true if the Cloud Spanner error being checked is a retryable error.
// isRetryable returns true if the Cloud Spanner error being checked is a
// retryable error.
func isRetryable(err error) bool {
if isErrorClosing(err) {
return true
@ -157,6 +162,7 @@ func extractRetryDelay(err error) (time.Duration, bool) {
// runRetryable keeps attempting to run f until one of the following happens:
// 1) f returns nil error or an unretryable error;
// 2) context is cancelled or timeout.
//
// TODO: consider using https://github.com/googleapis/gax-go/v2 once it
// becomes available internally.
func runRetryable(ctx context.Context, f func(context.Context) error) error {
@ -170,9 +176,9 @@ func runRetryableNoWrap(ctx context.Context, f func(context.Context) error) erro
for {
select {
case <-ctx.Done():
// Do context check here so that even f() failed to do
// so (for example, gRPC implementation bug), the loop
// can still have a chance to exit as expected.
// Do context check here so that even f() failed to do so (for
// example, gRPC implementation bug), the loop can still have a
// chance to exit as expected.
return errContextCanceled(ctx, funcErr)
default:
}

View file

@ -33,15 +33,19 @@ import (
"google.golang.org/grpc/metadata"
)
// sessionHandle is an interface for transactions to access Cloud Spanner sessions safely. It is generated by sessionPool.take().
// sessionHandle is an interface for transactions to access Cloud Spanner
// sessions safely. It is generated by sessionPool.take().
type sessionHandle struct {
// mu guarantees that the inner session object is returned / destroyed only once.
// mu guarantees that the inner session object is returned / destroyed only
// once.
mu sync.Mutex
// session is a pointer to a session object. Transactions never need to access it directly.
// session is a pointer to a session object. Transactions never need to
// access it directly.
session *session
}
// recycle gives the inner session object back to its home session pool. It is safe to call recycle multiple times but only the first one would take effect.
// recycle gives the inner session object back to its home session pool. It is
// safe to call recycle multiple times but only the first one would take effect.
func (sh *sessionHandle) recycle() {
sh.mu.Lock()
defer sh.mu.Unlock()
@ -53,7 +57,8 @@ func (sh *sessionHandle) recycle() {
sh.session = nil
}
// getID gets the Cloud Spanner session ID from the internal session object. getID returns empty string if the sessionHandle is nil or the inner session
// getID gets the Cloud Spanner session ID from the internal session object.
// getID returns empty string if the sessionHandle is nil or the inner session
// object has been released by recycle / destroy.
func (sh *sessionHandle) getID() string {
sh.mu.Lock()
@ -65,7 +70,8 @@ func (sh *sessionHandle) getID() string {
return sh.session.getID()
}
// getClient gets the Cloud Spanner RPC client associated with the session ID in sessionHandle.
// getClient gets the Cloud Spanner RPC client associated with the session ID
// in sessionHandle.
func (sh *sessionHandle) getClient() sppb.SpannerClient {
sh.mu.Lock()
defer sh.mu.Unlock()
@ -95,7 +101,8 @@ func (sh *sessionHandle) getTransactionID() transactionID {
return sh.session.tx
}
// destroy destroys the inner session object. It is safe to call destroy multiple times and only the first call would attempt to
// destroy destroys the inner session object. It is safe to call destroy
// multiple times and only the first call would attempt to
// destroy the inner session object.
func (sh *sessionHandle) destroy() {
sh.mu.Lock()
@ -103,39 +110,50 @@ func (sh *sessionHandle) destroy() {
sh.session = nil
sh.mu.Unlock()
if s == nil {
// sessionHandle has already been destroyed.
// sessionHandle has already been destroyed..
return
}
s.destroy(false)
}
// session wraps a Cloud Spanner session ID through which transactions are created and executed.
// session wraps a Cloud Spanner session ID through which transactions are
// created and executed.
type session struct {
// client is the RPC channel to Cloud Spanner. It is set only once during session's creation.
// client is the RPC channel to Cloud Spanner. It is set only once during
// session's creation.
client sppb.SpannerClient
// id is the unique id of the session in Cloud Spanner. It is set only once during session's creation.
// id is the unique id of the session in Cloud Spanner. It is set only once
// during session's creation.
id string
// pool is the session's home session pool where it was created. It is set only once during session's creation.
// pool is the session's home session pool where it was created. It is set
// only once during session's creation.
pool *sessionPool
// createTime is the timestamp of the session's creation. It is set only once during session's creation.
// createTime is the timestamp of the session's creation. It is set only
// once during session's creation.
createTime time.Time
// mu protects the following fields from concurrent access: both healthcheck workers and transactions can modify them.
// mu protects the following fields from concurrent access: both
// healthcheck workers and transactions can modify them.
mu sync.Mutex
// valid marks the validity of a session.
valid bool
// hcIndex is the index of the session inside the global healthcheck queue. If hcIndex < 0, session has been unregistered from the queue.
// hcIndex is the index of the session inside the global healthcheck queue.
// If hcIndex < 0, session has been unregistered from the queue.
hcIndex int
// idleList is the linkedlist node which links the session to its home session pool's idle list. If idleList == nil, the
// idleList is the linkedlist node which links the session to its home
// session pool's idle list. If idleList == nil, the
// session is not in idle list.
idleList *list.Element
// nextCheck is the timestamp of next scheduled healthcheck of the session. It is maintained by the global health checker.
// nextCheck is the timestamp of next scheduled healthcheck of the session.
// It is maintained by the global health checker.
nextCheck time.Time
// checkingHelath is true if currently this session is being processed by health checker. Must be modified under health checker lock.
// checkingHelath is true if currently this session is being processed by
// health checker. Must be modified under health checker lock.
checkingHealth bool
// md is the Metadata to be sent with each request.
md metadata.MD
// tx contains the transaction id if the session has been prepared for write.
// tx contains the transaction id if the session has been prepared for
// write.
tx transactionID
}
@ -166,12 +184,14 @@ func (s *session) ping() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
return runRetryable(ctx, func(ctx context.Context) error {
_, err := s.client.GetSession(contextWithOutgoingMetadata(ctx, s.pool.md), &sppb.GetSessionRequest{Name: s.getID()}) // s.getID is safe even when s is invalid.
// s.getID is safe even when s is invalid.
_, err := s.client.GetSession(contextWithOutgoingMetadata(ctx, s.pool.md), &sppb.GetSessionRequest{Name: s.getID()})
return err
})
}
// setHcIndex atomically sets the session's index in the healthcheck queue and returns the old index.
// setHcIndex atomically sets the session's index in the healthcheck queue and
// returns the old index.
func (s *session) setHcIndex(i int) int {
s.mu.Lock()
defer s.mu.Unlock()
@ -180,7 +200,8 @@ func (s *session) setHcIndex(i int) int {
return oi
}
// setIdleList atomically sets the session's idle list link and returns the old link.
// setIdleList atomically sets the session's idle list link and returns the old
// link.
func (s *session) setIdleList(le *list.Element) *list.Element {
s.mu.Lock()
defer s.mu.Unlock()
@ -212,14 +233,16 @@ func (s *session) setTransactionID(tx transactionID) {
s.tx = tx
}
// getID returns the session ID which uniquely identifies the session in Cloud Spanner.
// getID returns the session ID which uniquely identifies the session in Cloud
// Spanner.
func (s *session) getID() string {
s.mu.Lock()
defer s.mu.Unlock()
return s.id
}
// getHcIndex returns the session's index into the global healthcheck priority queue.
// getHcIndex returns the session's index into the global healthcheck priority
// queue.
func (s *session) getHcIndex() int {
s.mu.Lock()
defer s.mu.Unlock()
@ -244,12 +267,14 @@ func (s *session) getNextCheck() time.Time {
func (s *session) recycle() {
s.setTransactionID(nil)
if !s.pool.recycle(s) {
// s is rejected by its home session pool because it expired and the session pool currently has enough open sessions.
// s is rejected by its home session pool because it expired and the
// session pool currently has enough open sessions.
s.destroy(false)
}
}
// destroy removes the session from its home session pool, healthcheck queue and Cloud Spanner service.
// destroy removes the session from its home session pool, healthcheck queue
// and Cloud Spanner service.
func (s *session) destroy(isExpire bool) bool {
// Remove s from session pool.
if !s.pool.remove(s, isExpire) {
@ -265,8 +290,9 @@ func (s *session) destroy(isExpire bool) bool {
}
func (s *session) delete(ctx context.Context) {
// Ignore the error returned by runRetryable because even if we fail to explicitly destroy the session,
// it will be eventually garbage collected by Cloud Spanner.
// Ignore the error returned by runRetryable because even if we fail to
// explicitly destroy the session, it will be eventually garbage collected
// by Cloud Spanner.
err := runRetryable(ctx, func(ctx context.Context) error {
_, e := s.client.DeleteSession(ctx, &sppb.DeleteSessionRequest{Name: s.getID()})
return e
@ -276,7 +302,8 @@ func (s *session) delete(ctx context.Context) {
}
}
// prepareForWrite prepares the session for write if it is not already in that state.
// prepareForWrite prepares the session for write if it is not already in that
// state.
func (s *session) prepareForWrite(ctx context.Context) error {
if s.isWritePrepared() {
return nil
@ -291,31 +318,61 @@ func (s *session) prepareForWrite(ctx context.Context) error {
// SessionPoolConfig stores configurations of a session pool.
type SessionPoolConfig struct {
// getRPCClient is the caller supplied method for getting a gRPC client to Cloud Spanner, this makes session pool able to use client pooling.
// getRPCClient is the caller supplied method for getting a gRPC client to
// Cloud Spanner, this makes session pool able to use client pooling.
getRPCClient func() (sppb.SpannerClient, error)
// MaxOpened is the maximum number of opened sessions allowed by the session
// pool. Defaults to NumChannels * 100. If the client tries to open a session and
// there are already MaxOpened sessions, it will block until one becomes
// available or the context passed to the client method is canceled or times out.
// pool. If the client tries to open a session and there are already
// MaxOpened sessions, it will block until one becomes available or the
// context passed to the client method is canceled or times out.
//
// Defaults to NumChannels * 100.
MaxOpened uint64
// MinOpened is the minimum number of opened sessions that the session pool
// tries to maintain. Session pool won't continue to expire sessions if number
// of opened connections drops below MinOpened. However, if a session is found
// to be broken, it will still be evicted from the session pool, therefore it is
// posssible that the number of opened sessions drops below MinOpened.
// tries to maintain. Session pool won't continue to expire sessions if
// number of opened connections drops below MinOpened. However, if a session
// is found to be broken, it will still be evicted from the session pool,
// therefore it is posssible that the number of opened sessions drops below
// MinOpened.
//
// Defaults to 0.
MinOpened uint64
// MaxIdle is the maximum number of idle sessions, pool is allowed to keep. Defaults to 0.
// MaxIdle is the maximum number of idle sessions, pool is allowed to keep.
//
// Defaults to 0.
MaxIdle uint64
// MaxBurst is the maximum number of concurrent session creation requests. Defaults to 10.
// MaxBurst is the maximum number of concurrent session creation requests.
//
// Defaults to 10.
MaxBurst uint64
// WriteSessions is the fraction of sessions we try to keep prepared for write.
// WriteSessions is the fraction of sessions we try to keep prepared for
// write.
//
// Defaults to 0.
WriteSessions float64
// HealthCheckWorkers is number of workers used by health checker for this pool.
// HealthCheckWorkers is number of workers used by health checker for this
// pool.
//
// Defaults to 10.
HealthCheckWorkers int
// HealthCheckInterval is how often the health checker pings a session. Defaults to 5 min.
// HealthCheckInterval is how often the health checker pings a session.
//
// Defaults to 5m.
HealthCheckInterval time.Duration
// healthCheckSampleInterval is how often the health checker samples live session (for use in maintaining session pool size). Defaults to 1 min.
// healthCheckSampleInterval is how often the health checker samples live
// session (for use in maintaining session pool size).
//
// Defaults to 1m.
healthCheckSampleInterval time.Duration
// sessionLabels for the sessions created in the session pool.
sessionLabels map[string]string
}
@ -350,11 +407,13 @@ type sessionPool struct {
valid bool
// db is the database name that all sessions in the pool are associated with.
db string
// idleList caches idle session IDs. Session IDs in this list can be allocated for use.
// idleList caches idle session IDs. Session IDs in this list can be
// allocated for use.
idleList list.List
// idleWriteList caches idle sessions which have been prepared for write.
idleWriteList list.List
// mayGetSession is for broadcasting that session retrival/creation may proceed.
// mayGetSession is for broadcasting that session retrival/creation may
// proceed.
mayGetSession chan struct{}
// numOpened is the total number of open sessions from the session pool.
numOpened uint64
@ -383,9 +442,11 @@ func newSessionPool(db string, config SessionPoolConfig, md metadata.MD) (*sessi
md: md,
}
if config.HealthCheckWorkers == 0 {
// With 10 workers and assuming average latency of 5 ms for BeginTransaction, we will be able to
// prepare 2000 tx/sec in advance. If the rate of takeWriteSession is more than that, it will
// degrade to doing BeginTransaction inline.
// With 10 workers and assuming average latency of 5ms for
// BeginTransaction, we will be able to prepare 2000 tx/sec in advance.
// If the rate of takeWriteSession is more than that, it will degrade to
// doing BeginTransaction inline.
//
// TODO: consider resizing the worker pool dynamically according to the load.
config.HealthCheckWorkers = 10
}
@ -395,8 +456,10 @@ func newSessionPool(db string, config SessionPoolConfig, md metadata.MD) (*sessi
if config.healthCheckSampleInterval == 0 {
config.healthCheckSampleInterval = time.Minute
}
// On GCE VM, within the same region an healthcheck ping takes on average 10ms to finish, given a 5 minutes interval and
// 10 healthcheck workers, a healthChecker can effectively mantain 100 checks_per_worker/sec * 10 workers * 300 seconds = 300K sessions.
// On GCE VM, within the same region an healthcheck ping takes on average
// 10ms to finish, given a 5 minutes interval and 10 healthcheck workers, a
// healthChecker can effectively mantain
// 100 checks_per_worker/sec * 10 workers * 300 seconds = 300K sessions.
pool.hc = newHealthChecker(config.HealthCheckInterval, config.HealthCheckWorkers, config.healthCheckSampleInterval, pool)
close(pool.hc.ready)
return pool, nil
@ -440,7 +503,8 @@ func errInvalidSessionPool() error {
return spannerErrorf(codes.InvalidArgument, "invalid session pool")
}
// errGetSessionTimeout returns error for context timeout during sessionPool.take().
// errGetSessionTimeout returns error for context timeout during
// sessionPool.take().
func errGetSessionTimeout() error {
return spannerErrorf(codes.Canceled, "timeout / context canceled during getting session")
}
@ -473,7 +537,8 @@ func (p *sessionPool) createSession(ctx context.Context) (*session, error) {
s, err := createSession(ctx, sc, p.db, p.sessionLabels, p.md)
if err != nil {
doneCreate(false)
// Should return error directly because of the previous retries on CreateSession RPC.
// Should return error directly because of the previous retries on
// CreateSession RPC.
return nil, err
}
s.pool = p
@ -515,8 +580,9 @@ func (p *sessionPool) isHealthy(s *session) bool {
return true
}
// take returns a cached session if there are available ones; if there isn't any, it tries to allocate a new one.
// Session returned by take should be used for read operations.
// take returns a cached session if there are available ones; if there isn't
// any, it tries to allocate a new one. Session returned by take should be used
// for read operations.
func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
trace.TracePrintf(ctx, nil, "Acquiring a read-only session")
ctx = contextWithOutgoingMetadata(ctx, p.md)
@ -532,7 +598,8 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
return nil, errInvalidSessionPool()
}
if p.idleList.Len() > 0 {
// Idle sessions are available, get one from the top of the idle list.
// Idle sessions are available, get one from the top of the idle
// list.
s = p.idleList.Remove(p.idleList.Front()).(*session)
trace.TracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
"Acquired read-only session")
@ -544,15 +611,19 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
if s != nil {
s.setIdleList(nil)
p.mu.Unlock()
// From here, session is no longer in idle list, so healthcheck workers won't destroy it.
// If healthcheck workers failed to schedule healthcheck for the session timely, do the check here.
// Because session check is still much cheaper than session creation, they should be reused as much as possible.
// From here, session is no longer in idle list, so healthcheck
// workers won't destroy it. If healthcheck workers failed to
// schedule healthcheck for the session timely, do the check here.
// Because session check is still much cheaper than session
// creation, they should be reused as much as possible.
if !p.isHealthy(s) {
continue
}
return &sessionHandle{session: s}, nil
}
// Idle list is empty, block if session pool has reached max session creation concurrency or max number of open sessions.
// Idle list is empty, block if session pool has reached max session
// creation concurrency or max number of open sessions.
if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) {
mayGetSession := p.mayGetSession
p.mu.Unlock()
@ -565,6 +636,7 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
}
continue
}
// Take budget before the actual session creation.
p.numOpened++
recordStat(ctx, OpenSessionCount, int64(p.numOpened))
@ -580,8 +652,9 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
}
}
// takeWriteSession returns a write prepared cached session if there are available ones; if there isn't any, it tries to allocate a new one.
// Session returned should be used for read write transactions.
// takeWriteSession returns a write prepared cached session if there are
// available ones; if there isn't any, it tries to allocate a new one. Session
// returned should be used for read write transactions.
func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, error) {
trace.TracePrintf(ctx, nil, "Acquiring a read-write session")
ctx = contextWithOutgoingMetadata(ctx, p.md)
@ -597,7 +670,8 @@ func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, err
return nil, errInvalidSessionPool()
}
if p.idleWriteList.Len() > 0 {
// Idle sessions are available, get one from the top of the idle list.
// Idle sessions are available, get one from the top of the idle
// list.
s = p.idleWriteList.Remove(p.idleWriteList.Front()).(*session)
trace.TracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, "Acquired read-write session")
} else if p.idleList.Len() > 0 {
@ -607,14 +681,17 @@ func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, err
if s != nil {
s.setIdleList(nil)
p.mu.Unlock()
// From here, session is no longer in idle list, so healthcheck workers won't destroy it.
// If healthcheck workers failed to schedule healthcheck for the session timely, do the check here.
// Because session check is still much cheaper than session creation, they should be reused as much as possible.
// From here, session is no longer in idle list, so healthcheck
// workers won't destroy it. If healthcheck workers failed to
// schedule healthcheck for the session timely, do the check here.
// Because session check is still much cheaper than session
// creation, they should be reused as much as possible.
if !p.isHealthy(s) {
continue
}
} else {
// Idle list is empty, block if session pool has reached max session creation concurrency or max number of open sessions.
// Idle list is empty, block if session pool has reached max session
// creation concurrency or max number of open sessions.
if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) {
mayGetSession := p.mayGetSession
p.mu.Unlock()
@ -652,7 +729,8 @@ func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, err
}
}
// recycle puts session s back to the session pool's idle list, it returns true if the session pool successfully recycles session s.
// recycle puts session s back to the session pool's idle list, it returns true
// if the session pool successfully recycles session s.
func (p *sessionPool) recycle(s *session) bool {
p.mu.Lock()
defer p.mu.Unlock()
@ -660,7 +738,8 @@ func (p *sessionPool) recycle(s *session) bool {
// Reject the session if session is invalid or pool itself is invalid.
return false
}
// Put session at the back of the list to round robin for load balancing across channels.
// Put session at the back of the list to round robin for load balancing
// across channels.
if s.isWritePrepared() {
s.setIdleList(p.idleWriteList.PushBack(s))
} else {
@ -673,12 +752,14 @@ func (p *sessionPool) recycle(s *session) bool {
}
// remove atomically removes session s from the session pool and invalidates s.
// If isExpire == true, the removal is triggered by session expiration and in such cases, only idle sessions can be removed.
// If isExpire == true, the removal is triggered by session expiration and in
// such cases, only idle sessions can be removed.
func (p *sessionPool) remove(s *session, isExpire bool) bool {
p.mu.Lock()
defer p.mu.Unlock()
if isExpire && (p.numOpened <= p.MinOpened || s.getIdleList() == nil) {
// Don't expire session if the session is not in idle list (in use), or if number of open sessions is going below p.MinOpened.
// Don't expire session if the session is not in idle list (in use), or
// if number of open sessions is going below p.MinOpened.
return false
}
ol := s.setIdleList(nil)
@ -700,7 +781,8 @@ func (p *sessionPool) remove(s *session, isExpire bool) bool {
return false
}
// hcHeap implements heap.Interface. It is used to create the priority queue for session healthchecks.
// hcHeap implements heap.Interface. It is used to create the priority queue for
// session healthchecks.
type hcHeap struct {
sessions []*session
}
@ -741,9 +823,10 @@ func (h *hcHeap) Pop() interface{} {
// healthChecker performs periodical healthchecks on registered sessions.
type healthChecker struct {
// mu protects concurrent access to hcQueue.
// mu protects concurrent access to healthChecker.
mu sync.Mutex
// queue is the priority queue for session healthchecks. Sessions with lower nextCheck rank higher in the queue.
// queue is the priority queue for session healthchecks. Sessions with lower
// nextCheck rank higher in the queue.
queue hcHeap
// interval is the average interval between two healthchecks on a session.
interval time.Duration
@ -813,9 +896,11 @@ func (hc *healthChecker) getInterval() time.Duration {
return hc.interval
}
// scheduledHCLocked schedules next healthcheck on session s with the assumption that hc.mu is being held.
// scheduledHCLocked schedules next healthcheck on session s with the assumption
// that hc.mu is being held.
func (hc *healthChecker) scheduledHCLocked(s *session) {
// The next healthcheck will be scheduled after [interval*0.5, interval*1.5) nanoseconds.
// The next healthcheck will be scheduled after
// [interval*0.5, interval*1.5) ns.
nsFromNow := rand.Int63n(int64(hc.interval)) + int64(hc.interval)/2
s.setNextCheck(time.Now().Add(time.Duration(nsFromNow)))
if hi := s.getHcIndex(); hi != -1 {
@ -824,7 +909,8 @@ func (hc *healthChecker) scheduledHCLocked(s *session) {
}
}
// scheduledHC schedules next healthcheck on session s. It is safe to be called concurrently.
// scheduledHC schedules next healthcheck on session s. It is safe to be called
// concurrently.
func (hc *healthChecker) scheduledHC(s *session) {
hc.mu.Lock()
defer hc.mu.Unlock()
@ -870,7 +956,8 @@ func (hc *healthChecker) healthCheck(s *session) {
}
}
// worker performs the healthcheck on sessions in healthChecker's priority queue.
// worker performs the healthcheck on sessions in healthChecker's priority
// queue.
func (hc *healthChecker) worker(i int) {
// Returns a session which we should ping to keep it alive.
getNextForPing := func() *session {
@ -917,8 +1004,8 @@ func (hc *healthChecker) worker(i int) {
for {
if hc.isClosing() {
// Exit when the pool has been closed and all sessions have been destroyed
// or when health checker has been closed.
// Exit when the pool has been closed and all sessions have been
// destroyed or when health checker has been closed.
hc.waitWorkers.Done()
return
}
@ -928,7 +1015,8 @@ func (hc *healthChecker) worker(i int) {
err := ws.prepareForWrite(contextWithOutgoingMetadata(ctx, hc.pool.md))
cancel()
if err != nil {
// Skip handling prepare error, session can be prepared in next cycle
// Skip handling prepare error, session can be prepared in next
// cycle.
log.Printf("Failed to prepare session, error: %v", toSpannerError(err))
}
hc.pool.recycle(ws)
@ -940,7 +1028,7 @@ func (hc *healthChecker) worker(i int) {
rs := getNextForPing()
if rs == nil {
if ws == nil {
// No work to be done so sleep to avoid burning cpu
// No work to be done so sleep to avoid burning CPU.
pause := int64(100 * time.Millisecond)
if pause > int64(hc.interval) {
pause = int64(hc.interval)
@ -957,24 +1045,23 @@ func (hc *healthChecker) worker(i int) {
}
}
// maintainer maintains the maxSessionsInUse by a window of kWindowSize * sampleInterval.
// Based on this information, health checker will try to maintain the number of sessions by hc..
// maintainer maintains the maxSessionsInUse by a window of
// kWindowSize * sampleInterval. Based on this information, health checker will
// try to maintain the number of sessions by hc.
func (hc *healthChecker) maintainer() {
// Wait so that pool is ready.
<-hc.ready
var (
windowSize uint64 = 10
iteration uint64
)
windowSize := uint64(10)
for {
for iteration := uint64(0); ; iteration++ {
if hc.isClosing() {
hc.waitWorkers.Done()
return
}
// maxSessionsInUse is the maximum number of sessions in use concurrently over a period of time.
// maxSessionsInUse is the maximum number of sessions in use
// concurrently over a period of time.
var maxSessionsInUse uint64
// Updates metrics.
@ -994,8 +1081,10 @@ func (hc *healthChecker) maintainer() {
hc.mu.Unlock()
// Replenish or Shrink pool if needed.
// Note: we don't need to worry about pending create session requests, we only need to sample the current sessions in use.
// the routines will not try to create extra / delete creating sessions.
//
// Note: we don't need to worry about pending create session requests,
// we only need to sample the current sessions in use. The routines will
// not try to create extra / delete creating sessions.
if sessionsToKeep > currSessionsOpened {
hc.replenishPool(ctx, sessionsToKeep)
} else {
@ -1007,11 +1096,11 @@ func (hc *healthChecker) maintainer() {
case <-hc.done:
cancel()
}
iteration++
}
}
// replenishPool is run if numOpened is less than sessionsToKeep, timeouts on sampleInterval.
// replenishPool is run if numOpened is less than sessionsToKeep, timeouts on
// sampleInterval.
func (hc *healthChecker) replenishPool(ctx context.Context, sessionsToKeep uint64) {
for {
if ctx.Err() != nil {
@ -1080,13 +1169,16 @@ func (hc *healthChecker) shrinkPool(ctx context.Context, sessionsToKeep uint64)
}
}
// shouldDropSession returns true if a particular error leads to the removal of a session
// shouldDropSession returns true if a particular error leads to the removal of
// a session
func shouldDropSession(err error) bool {
if err == nil {
return false
}
// If a Cloud Spanner can no longer locate the session (for example, if session is garbage collected), then caller
// should not try to return the session back into the session pool.
// If a Cloud Spanner can no longer locate the session (for example, if
// session is garbage collected), then caller should not try to return the
// session back into the session pool.
//
// TODO: once gRPC can return auxiliary error information, stop parsing the error message.
if ErrCode(err) == codes.NotFound && strings.Contains(ErrDesc(err), "Session not found") {
return true
@ -1094,7 +1186,7 @@ func shouldDropSession(err error) bool {
return false
}
// maxUint64 returns the maximum of two uint64
// maxUint64 returns the maximum of two uint64.
func maxUint64(a, b uint64) uint64 {
if a > b {
return a
@ -1102,7 +1194,7 @@ func maxUint64(a, b uint64) uint64 {
return b
}
// minUint64 returns the minimum of two uint64
// minUint64 returns the minimum of two uint64.
func minUint64(a, b uint64) uint64 {
if a > b {
return b

View file

@ -78,7 +78,8 @@ func (s *Statement) convertParams() (*structpb.Struct, map[string]*sppb.Type, er
return params, paramTypes, nil
}
// errBindParam returns error for not being able to bind parameter to query request.
// errBindParam returns error for not being able to bind parameter to query
// request.
func errBindParam(k string, v interface{}, err error) error {
if err == nil {
return nil

View file

@ -66,13 +66,14 @@ const (
//
// Exact staleness
//
// An exact staleness timestamp bound executes reads at a user-specified timestamp.
// Reads at a timestamp are guaranteed to see a consistent prefix of the global
// transaction history: they observe modifications done by all transactions with a
// commit timestamp less than or equal to the read timestamp, and observe none of the
// modifications done by transactions with a larger commit timestamp. They will block
// until all conflicting transactions that may be assigned commit timestamps less
// than or equal to the read timestamp have finished.
// An exact staleness timestamp bound executes reads at a user-specified
// timestamp. Reads at a timestamp are guaranteed to see a consistent prefix of
// the global transaction history: they observe modifications done by all
// transactions with a commit timestamp less than or equal to the read
// timestamp, and observe none of the modifications done by transactions with a
// larger commit timestamp. They will block until all conflicting transactions
// that may be assigned commit timestamps less than or equal to the read
// timestamp have finished.
//
// The timestamp can either be expressed as an absolute Cloud Spanner commit
// timestamp or a staleness relative to the current time.
@ -86,10 +87,10 @@ const (
//
// Bounded staleness
//
// Bounded staleness modes allow Cloud Spanner to pick the read timestamp, subject to
// a user-provided staleness bound. Cloud Spanner chooses the newest timestamp within
// the staleness bound that allows execution of the reads at the closest
// available replica without blocking.
// Bounded staleness modes allow Cloud Spanner to pick the read timestamp,
// subject to a user-provided staleness bound. Cloud Spanner chooses the newest
// timestamp within the staleness bound that allows execution of the reads at
// the closest available replica without blocking.
//
// All rows yielded are consistent with each other: if any part of the read
// observes a transaction, all parts of the read see the transaction. Boundedly
@ -114,12 +115,12 @@ const (
//
// Old read timestamps and garbage collection
//
// Cloud Spanner continuously garbage collects deleted and overwritten data in the
// background to reclaim storage space. This process is known as "version
// Cloud Spanner continuously garbage collects deleted and overwritten data in
// the background to reclaim storage space. This process is known as "version
// GC". By default, version GC reclaims versions after they are four hours
// old. Because of this, Cloud Spanner cannot perform reads at read timestamps more
// than four hours in the past. This restriction also applies to in-progress
// reads and/or SQL queries whose timestamps become too old while
// old. Because of this, Cloud Spanner cannot perform reads at read timestamps
// more than four hours in the past. This restriction also applies to
// in-progress reads and/or SQL queries whose timestamps become too old while
// executing. Reads and SQL queries with too-old read timestamps fail with the
// error ErrorCode.FAILED_PRECONDITION.
type TimestampBound struct {
@ -197,8 +198,8 @@ func durationProto(d time.Duration) *pbd.Duration {
}
}
// timestampProto takes a time.Time and converts it into pbt.Timestamp for calling
// gRPC APIs.
// timestampProto takes a time.Time and converts it into pbt.Timestamp for
// calling gRPC APIs.
func timestampProto(t time.Time) *pbt.Timestamp {
return &pbt.Timestamp{
Seconds: t.Unix(),
@ -206,8 +207,9 @@ func timestampProto(t time.Time) *pbt.Timestamp {
}
}
// buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a sppb.TransactionOptions_ReadOnly
// transaction option, which is then used in transactional reads.
// buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a
// sppb.TransactionOptions_ReadOnly transaction option, which is then used in
// transactional reads.
func buildTransactionOptionsReadOnly(tb TimestampBound, returnReadTimestamp bool) *sppb.TransactionOptions_ReadOnly {
pb := &sppb.TransactionOptions_ReadOnly{
ReturnReadTimestamp: returnReadTimestamp,

View file

@ -30,25 +30,31 @@ import (
"google.golang.org/grpc/metadata"
)
// transactionID stores a transaction ID which uniquely identifies a transaction in Cloud Spanner.
// transactionID stores a transaction ID which uniquely identifies a transaction
// in Cloud Spanner.
type transactionID []byte
// txReadEnv manages a read-transaction environment consisting of a session handle and a transaction selector.
// txReadEnv manages a read-transaction environment consisting of a session
// handle and a transaction selector.
type txReadEnv interface {
// acquire returns a read-transaction environment that can be used to perform a transactional read.
// acquire returns a read-transaction environment that can be used to
// perform a transactional read.
acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error)
// sets the transaction's read timestamp
setTimestamp(time.Time)
// release should be called at the end of every transactional read to deal with session recycling.
// release should be called at the end of every transactional read to deal
// with session recycling.
release(error)
}
// txReadOnly contains methods for doing transactional reads.
type txReadOnly struct {
// read-transaction environment for performing transactional read operations.
// read-transaction environment for performing transactional read
// operations.
txReadEnv
sequenceNumber int64 // Atomic. Only needed for DML statements, but used for all.
// Atomic. Only needed for DML statements, but used forall.
sequenceNumber int64
}
// errSessionClosed returns error for using a recycled/destroyed session
@ -69,17 +75,18 @@ func (t *txReadOnly) ReadUsingIndex(ctx context.Context, table, index string, ke
// ReadOptions provides options for reading rows from a database.
type ReadOptions struct {
// The index to use for reading. If non-empty, you can only read columns that are
// part of the index key, part of the primary key, or stored in the index due to
// a STORING clause in the index definition.
// The index to use for reading. If non-empty, you can only read columns
// that are part of the index key, part of the primary key, or stored in the
// index due to a STORING clause in the index definition.
Index string
// The maximum number of rows to read. A limit value less than 1 means no limit.
// The maximum number of rows to read. A limit value less than 1 means no
// limit.
Limit int
}
// ReadWithOptions returns a RowIterator for reading multiple rows from the database.
// Pass a ReadOptions to modify the read operation.
// ReadWithOptions returns a RowIterator for reading multiple rows from the
// database. Pass a ReadOptions to modify the read operation.
func (t *txReadOnly) ReadWithOptions(ctx context.Context, table string, keys KeySet, columns []string, opts *ReadOptions) (ri *RowIterator) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/spanner.Read")
defer func() { trace.EndSpan(ctx, ri.err) }()
@ -129,7 +136,8 @@ func (t *txReadOnly) ReadWithOptions(ctx context.Context, table string, keys Key
)
}
// errRowNotFound returns error for not being able to read the row identified by key.
// errRowNotFound returns error for not being able to read the row identified by
// key.
func errRowNotFound(table string, key Key) error {
return spannerErrorf(codes.NotFound, "row not found(Table: %v, PrimaryKey: %v)", table, key)
}
@ -152,12 +160,12 @@ func (t *txReadOnly) ReadRow(ctx context.Context, table string, key Key, columns
}
}
// Query executes a query against the database. It returns a RowIterator
// for retrieving the resulting rows.
// Query executes a query against the database. It returns a RowIterator for
// retrieving the resulting rows.
//
// Query returns only row data, without a query plan or execution statistics.
// Use QueryWithStats to get rows along with the plan and statistics.
// Use AnalyzeQuery to get just the plan.
// Use QueryWithStats to get rows along with the plan and statistics. Use
// AnalyzeQuery to get just the plan.
func (t *txReadOnly) Query(ctx context.Context, statement Statement) *RowIterator {
return t.query(ctx, statement, sppb.ExecuteSqlRequest_NORMAL)
}
@ -237,7 +245,7 @@ func (t *txReadOnly) prepareExecuteSQL(ctx context.Context, stmt Statement, mode
type txState int
const (
// transaction is new, waiting to be initialized.
// transaction is new, waiting to be initialized..
txNew txState = iota
// transaction is being initialized.
txInit
@ -247,7 +255,8 @@ const (
txClosed
)
// errRtsUnavailable returns error for read transaction's read timestamp being unavailable.
// errRtsUnavailable returns error for read transaction's read timestamp being
// unavailable.
func errRtsUnavailable() error {
return spannerErrorf(codes.Internal, "read timestamp is unavailable")
}
@ -263,35 +272,37 @@ func errUnexpectedTxState(ts txState) error {
}
// ReadOnlyTransaction provides a snapshot transaction with guaranteed
// consistency across reads, but does not allow writes. Read-only
// transactions can be configured to read at timestamps in the past.
// consistency across reads, but does not allow writes. Read-only transactions
// can be configured to read at timestamps in the past.
//
// Read-only transactions do not take locks. Instead, they work by choosing a
// Cloud Spanner timestamp, then executing all reads at that timestamp. Since they do
// not acquire locks, they do not block concurrent read-write transactions.
// Cloud Spanner timestamp, then executing all reads at that timestamp. Since
// they do not acquire locks, they do not block concurrent read-write
// transactions.
//
// Unlike locking read-write transactions, read-only transactions never
// abort. They can fail if the chosen read timestamp is garbage collected;
// however, the default garbage collection policy is generous enough that most
// applications do not need to worry about this in practice. See the
// documentation of TimestampBound for more details.
// Unlike locking read-write transactions, read-only transactions never abort.
// They can fail if the chosen read timestamp is garbage collected; however, the
// default garbage collection policy is generous enough that most applications
// do not need to worry about this in practice. See the documentation of
// TimestampBound for more details.
//
// A ReadOnlyTransaction consumes resources on the server until Close is
// called.
// A ReadOnlyTransaction consumes resources on the server until Close is called.
type ReadOnlyTransaction struct {
// txReadOnly contains methods for performing transactional reads.
txReadOnly
// singleUse indicates that the transaction can be used for only one read.
singleUse bool
// sp is the session pool for allocating a session to execute the read-only transaction. It is set only once during initialization of the ReadOnlyTransaction.
sp *sessionPool
// mu protects concurrent access to the internal states of ReadOnlyTransaction.
mu sync.Mutex
// tx is the transaction ID in Cloud Spanner that uniquely identifies the ReadOnlyTransaction.
// txReadOnly contains methods for performing transactional reads.
txReadOnly
// singleUse indicates that the transaction can be used for only one read.
singleUse bool
// sp is the session pool for allocating a session to execute the read-only
// transaction. It is set only once during initialization of the
// ReadOnlyTransaction.
sp *sessionPool
// tx is the transaction ID in Cloud Spanner that uniquely identifies the
// ReadOnlyTransaction.
tx transactionID
// txReadyOrClosed is for broadcasting that transaction ID has been returned by Cloud Spanner or that transaction is closed.
// txReadyOrClosed is for broadcasting that transaction ID has been returned
// by Cloud Spanner or that transaction is closed.
txReadyOrClosed chan struct{}
// state is the current transaction status of the ReadOnly transaction.
state txState
@ -303,12 +314,14 @@ type ReadOnlyTransaction struct {
tb TimestampBound
}
// errTxInitTimeout returns error for timeout in waiting for initialization of the transaction.
// errTxInitTimeout returns error for timeout in waiting for initialization of
// the transaction.
func errTxInitTimeout() error {
return spannerErrorf(codes.Canceled, "timeout/context canceled in waiting for transaction's initialization")
}
// getTimestampBound returns the read staleness bound specified for the ReadOnlyTransaction.
// getTimestampBound returns the read staleness bound specified for the
// ReadOnlyTransaction.
func (t *ReadOnlyTransaction) getTimestampBound() TimestampBound {
t.mu.Lock()
defer t.mu.Unlock()
@ -327,7 +340,8 @@ func (t *ReadOnlyTransaction) begin(ctx context.Context) error {
defer func() {
if !locked {
t.mu.Lock()
// Not necessary, just to make it clear that t.mu is being held when locked == true.
// Not necessary, just to make it clear that t.mu is being held when
// locked == true.
locked = true
}
if t.state != txClosed {
@ -337,7 +351,8 @@ func (t *ReadOnlyTransaction) begin(ctx context.Context) error {
}
t.mu.Unlock()
if err != nil && sh != nil {
// Got a valid session handle, but failed to initialize transaction on Cloud Spanner.
// Got a valid session handle, but failed to initialize transaction=
// on Cloud Spanner.
if shouldDropSession(err) {
sh.destroy()
}
@ -368,11 +383,17 @@ func (t *ReadOnlyTransaction) begin(ctx context.Context) error {
return nil
})
t.mu.Lock()
locked = true // defer function will be executed with t.mu being held.
if t.state == txClosed { // During the execution of t.begin(), t.Close() was invoked.
// defer function will be executed with t.mu being held.
locked = true
// During the execution of t.begin(), t.Close() was invoked.
if t.state == txClosed {
return errSessionClosed(sh)
}
// If begin() fails, this allows other queries to take over the initialization.
// If begin() fails, this allows other queries to take over the
// initialization.
t.tx = nil
if err == nil {
t.tx = tx
@ -417,12 +438,16 @@ func (t *ReadOnlyTransaction) acquireSingleUse(ctx context.Context) (*sessionHan
if err != nil {
return nil, nil, err
}
// Install session handle into t, which can be used for readonly operations later.
// Install session handle into t, which can be used for readonly
// operations later.
t.sh = sh
return sh, ts, nil
}
us := t.state
// SingleUse transaction should only be in either txNew state or txClosed state.
// SingleUse transaction should only be in either txNew state or txClosed
// state.
return nil, nil, errUnexpectedTxState(us)
}
@ -434,7 +459,8 @@ func (t *ReadOnlyTransaction) acquireMultiUse(ctx context.Context) (*sessionHand
t.mu.Unlock()
return nil, nil, errTxClosed()
case txNew:
// State transit to txInit so that no further TimestampBound change is accepted.
// State transit to txInit so that no further TimestampBound change
// is accepted.
t.state = txInit
t.mu.Unlock()
continue
@ -448,7 +474,8 @@ func (t *ReadOnlyTransaction) acquireMultiUse(ctx context.Context) (*sessionHand
// Need to check transaction state again.
continue
case <-ctx.Done():
// The waiting for initialization is timeout, return error directly.
// The waiting for initialization is timeout, return error
// directly.
return nil, nil, errTxInitTimeout()
}
}
@ -456,12 +483,17 @@ func (t *ReadOnlyTransaction) acquireMultiUse(ctx context.Context) (*sessionHand
t.tx = transactionID{}
t.mu.Unlock()
// Begin a read-only transaction.
// TODO: consider adding a transaction option which allow queries to initiate transactions by themselves. Note that this option might not be
// always good because the ID of the new transaction won't be ready till the query returns some data or completes.
//
// TODO: consider adding a transaction option which allow queries to
// initiate transactions by themselves. Note that this option might
// not be always good because the ID of the new transaction won't
// be ready till the query returns some data or completes.
if err := t.begin(ctx); err != nil {
return nil, nil, err
}
// If t.begin() succeeded, t.state should have been changed to txActive, so we can just continue here.
// If t.begin() succeeded, t.state should have been changed to
// txActive, so we can just continue here.
continue
case txActive:
sh := t.sh
@ -503,7 +535,8 @@ func (t *ReadOnlyTransaction) release(err error) {
}
}
// Close closes a ReadOnlyTransaction, the transaction cannot perform any reads after being closed.
// Close closes a ReadOnlyTransaction, the transaction cannot perform any reads
// after being closed.
func (t *ReadOnlyTransaction) Close() {
if t.singleUse {
return
@ -518,18 +551,18 @@ func (t *ReadOnlyTransaction) Close() {
if sh == nil {
return
}
// If session handle is already destroyed, this becomes a noop.
// If there are still active queries and if the recycled session is reused before they complete, Cloud Spanner will cancel them
// on behalf of the new transaction on the session.
// If session handle is already destroyed, this becomes a noop. If there are
// still active queries and if the recycled session is reused before they
// complete, Cloud Spanner will cancel them on behalf of the new transaction
// on the session.
if sh != nil {
sh.recycle()
}
}
// Timestamp returns the timestamp chosen to perform reads and
// queries in this transaction. The value can only be read after some
// read or query has either returned some data or completed without
// returning any data.
// Timestamp returns the timestamp chosen to perform reads and queries in this
// transaction. The value can only be read after some read or query has either
// returned some data or completed without returning any data.
func (t *ReadOnlyTransaction) Timestamp() (time.Time, error) {
t.mu.Lock()
defer t.mu.Unlock()
@ -583,24 +616,24 @@ func (t *ReadOnlyTransaction) WithTimestampBound(tb TimestampBound) *ReadOnlyTra
//
// Semantics
//
// Cloud Spanner can commit the transaction if all read locks it acquired are still
// valid at commit time, and it is able to acquire write locks for all
// Cloud Spanner can commit the transaction if all read locks it acquired are
// still valid at commit time, and it is able to acquire write locks for all
// writes. Cloud Spanner can abort the transaction for any reason. If a commit
// attempt returns ABORTED, Cloud Spanner guarantees that the transaction has not
// modified any user data in Cloud Spanner.
// attempt returns ABORTED, Cloud Spanner guarantees that the transaction has
// not modified any user data in Cloud Spanner.
//
// Unless the transaction commits, Cloud Spanner makes no guarantees about how long
// the transaction's locks were held for. It is an error to use Cloud Spanner locks
// for any sort of mutual exclusion other than between Cloud Spanner transactions
// themselves.
// Unless the transaction commits, Cloud Spanner makes no guarantees about how
// long the transaction's locks were held for. It is an error to use Cloud
// Spanner locks for any sort of mutual exclusion other than between Cloud
// Spanner transactions themselves.
//
// Aborted transactions
//
// Application code does not need to retry explicitly; RunInTransaction will
// automatically retry a transaction if an attempt results in an abort. The
// lock priority of a transaction increases after each prior aborted
// transaction, meaning that the next attempt has a slightly better chance of
// success than before.
// automatically retry a transaction if an attempt results in an abort. The lock
// priority of a transaction increases after each prior aborted transaction,
// meaning that the next attempt has a slightly better chance of success than
// before.
//
// Under some circumstances (e.g., many transactions attempting to modify the
// same row(s)), a transaction can abort many times in a short period before
@ -612,8 +645,8 @@ func (t *ReadOnlyTransaction) WithTimestampBound(tb TimestampBound) *ReadOnlyTra
//
// A transaction is considered idle if it has no outstanding reads or SQL
// queries and has not started a read or SQL query within the last 10
// seconds. Idle transactions can be aborted by Cloud Spanner so that they don't hold
// on to locks indefinitely. In that case, the commit will fail with error
// seconds. Idle transactions can be aborted by Cloud Spanner so that they don't
// hold on to locks indefinitely. In that case, the commit will fail with error
// ABORTED.
//
// If this behavior is undesirable, periodically executing a simple SQL query
@ -622,12 +655,15 @@ func (t *ReadOnlyTransaction) WithTimestampBound(tb TimestampBound) *ReadOnlyTra
type ReadWriteTransaction struct {
// txReadOnly contains methods for performing transactional reads.
txReadOnly
// sh is the sessionHandle allocated from sp. It is set only once during the initialization of ReadWriteTransaction.
// sh is the sessionHandle allocated from sp. It is set only once during the
// initialization of ReadWriteTransaction.
sh *sessionHandle
// tx is the transaction ID in Cloud Spanner that uniquely identifies the ReadWriteTransaction.
// It is set only once in ReadWriteTransaction.begin() during the initialization of ReadWriteTransaction.
// tx is the transaction ID in Cloud Spanner that uniquely identifies the
// ReadWriteTransaction. It is set only once in ReadWriteTransaction.begin()
// during the initialization of ReadWriteTransaction.
tx transactionID
// mu protects concurrent access to the internal states of ReadWriteTransaction.
// mu protects concurrent access to the internal states of
// ReadWriteTransaction.
mu sync.Mutex
// state is the current transaction status of the read-write transaction.
state txState
@ -637,9 +673,9 @@ type ReadWriteTransaction struct {
// BufferWrite adds a list of mutations to the set of updates that will be
// applied when the transaction is committed. It does not actually apply the
// write until the transaction is committed, so the operation does not
// block. The effects of the write won't be visible to any reads (including
// reads done in the same transaction) until the transaction commits.
// write until the transaction is committed, so the operation does not block.
// The effects of the write won't be visible to any reads (including reads done
// in the same transaction) until the transaction commits.
//
// See the example for Client.ReadWriteTransaction.
func (t *ReadWriteTransaction) BufferWrite(ms []*Mutation) error {
@ -655,10 +691,10 @@ func (t *ReadWriteTransaction) BufferWrite(ms []*Mutation) error {
return nil
}
// Update executes a DML statement against the database. It returns the number of
// affected rows.
// Update returns an error if the statement is a query. However, the
// query is executed, and any data read will be validated upon commit.
// Update executes a DML statement against the database. It returns the number
// of affected rows. Update returns an error if the statement is a query.
// However, the query is executed, and any data read will be validated upon
// commit.
func (t *ReadWriteTransaction) Update(ctx context.Context, stmt Statement) (rowCount int64, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/spanner.Update")
defer func() { trace.EndSpan(ctx, err) }()
@ -785,7 +821,8 @@ func beginTransaction(ctx context.Context, sid string, client sppb.SpannerClient
return tx, nil
}
// begin starts a read-write transacton on Cloud Spanner, it is always called before any of the public APIs.
// begin starts a read-write transacton on Cloud Spanner, it is always called
// before any of the public APIs.
func (t *ReadWriteTransaction) begin(ctx context.Context) error {
if t.tx != nil {
t.state = txActive
@ -803,7 +840,8 @@ func (t *ReadWriteTransaction) begin(ctx context.Context) error {
return err
}
// commit tries to commit a readwrite transaction to Cloud Spanner. It also returns the commit timestamp for the transactions.
// commit tries to commit a readwrite transaction to Cloud Spanner. It also
// returns the commit timestamp for the transactions.
func (t *ReadWriteTransaction) commit(ctx context.Context) (time.Time, error) {
var ts time.Time
t.mu.Lock()
@ -813,7 +851,8 @@ func (t *ReadWriteTransaction) commit(ctx context.Context) (time.Time, error) {
if err != nil {
return ts, err
}
// In case that sessionHandle was destroyed but transaction body fails to report it.
// In case that sessionHandle was destroyed but transaction body fails to
// report it.
sid, client := t.sh.getID(), t.sh.getClient()
if sid == "" || client == nil {
return ts, errSessionClosed(t.sh)
@ -841,13 +880,15 @@ func (t *ReadWriteTransaction) commit(ctx context.Context) (time.Time, error) {
return ts, err
}
// rollback is called when a commit is aborted or the transaction body runs into error.
// rollback is called when a commit is aborted or the transaction body runs
// into error.
func (t *ReadWriteTransaction) rollback(ctx context.Context) {
t.mu.Lock()
// Forbid further operations on rollbacked transaction.
t.state = txClosed
t.mu.Unlock()
// In case that sessionHandle was destroyed but transaction body fails to report it.
// In case that sessionHandle was destroyed but transaction body fails to
// report it.
sid, client := t.sh.getID(), t.sh.getClient()
if sid == "" || client == nil {
return
@ -877,11 +918,13 @@ func (t *ReadWriteTransaction) runInTransaction(ctx context.Context, f func(cont
if err != nil {
if isAbortErr(err) {
// Retry the transaction using the same session on ABORT error.
// Cloud Spanner will create the new transaction with the previous one's wound-wait priority.
// Cloud Spanner will create the new transaction with the previous
// one's wound-wait priority.
err = errRetry(err)
return ts, err
}
// Not going to commit, according to API spec, should rollback the transaction.
// Not going to commit, according to API spec, should rollback the
// transaction.
t.rollback(ctx)
return ts, err
}
@ -889,13 +932,17 @@ func (t *ReadWriteTransaction) runInTransaction(ctx context.Context, f func(cont
return ts, nil
}
// writeOnlyTransaction provides the most efficient way of doing write-only transactions. It essentially does blind writes to Cloud Spanner.
// writeOnlyTransaction provides the most efficient way of doing write-only
// transactions. It essentially does blind writes to Cloud Spanner.
type writeOnlyTransaction struct {
// sp is the session pool which writeOnlyTransaction uses to get Cloud Spanner sessions for blind writes.
// sp is the session pool which writeOnlyTransaction uses to get Cloud
// Spanner sessions for blind writes.
sp *sessionPool
}
// applyAtLeastOnce commits a list of mutations to Cloud Spanner at least once, unless one of the following happens:
// applyAtLeastOnce commits a list of mutations to Cloud Spanner at least once,
// unless one of the following happens:
//
// 1) Context times out.
// 2) An unretryable error (e.g. database not found) occurs.
// 3) There is a malformed Mutation object.
@ -916,7 +963,8 @@ func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Muta
// No usable session for doing the commit, take one from pool.
sh, e = t.sp.take(ctx)
if e != nil {
// sessionPool.Take already retries for session creations/retrivals.
// sessionPool.Take already retries for session
// creations/retrivals.
return e
}
}
@ -933,7 +981,8 @@ func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Muta
}, grpc.Trailer(&trailers))
if e != nil {
if isAbortErr(e) {
// Mask ABORT error as retryable, because aborted transactions are allowed to be retried.
// Mask ABORT error as retryable, because aborted transactions
// are allowed to be retried.
return errRetry(toSpannerErrorWithMetadata(e, trailers))
}
if shouldDropSession(e) {
@ -953,7 +1002,8 @@ func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Muta
return ts, err
}
// isAbortedErr returns true if the error indicates that an gRPC call is aborted on the server side.
// isAbortedErr returns true if the error indicates that an gRPC call is
// aborted on the server side.
func isAbortErr(err error) bool {
if err == nil {
return false

View file

@ -35,12 +35,11 @@ import (
const commitTimestampPlaceholderString = "spanner.commit_timestamp()"
var (
// CommitTimestamp is a special value used to tell Cloud Spanner
// to insert the commit timestamp of the transaction into a column.
// It can be used in a Mutation, or directly used in
// InsertStruct or InsertMap. See ExampleCommitTimestamp.
// This is just a placeholder and the actual value stored in this
// variable has no meaning.
// CommitTimestamp is a special value used to tell Cloud Spanner to insert
// the commit timestamp of the transaction into a column. It can be used in
// a Mutation, or directly used in InsertStruct or InsertMap. See
// ExampleCommitTimestamp. This is just a placeholder and the actual value
// stored in this variable has no meaning.
CommitTimestamp = commitTimestamp
commitTimestamp = time.Unix(0, 0).In(time.FixedZone("CommitTimestamp placeholder", 0xDB))
)
@ -79,25 +78,6 @@ type NullFloat64 struct {
Valid bool // Valid is true if Float64 is not NULL.
}
// Cloud Spanner STRUCT (aka STRUCT) values (https://cloud.google.com/spanner/docs/data-types#struct-type)
// can be represented by a Go struct value.
// The spanner.StructType of such values is built from the field types and field tag information
// of the Go struct. If a field in the struct type definition has a "spanner:<field_name>" tag,
// then the value of the "spanner" key in the tag is used as the name for that field in the
// built spanner.StructType, otherwise the field name in the struct definition is used. To specify a
// field with an empty field name in a Cloud Spanner STRUCT type, use the `spanner:""` tag
// annotation against the corresponding field in the Go struct's type definition.
//
// A STRUCT value can contain STRUCT-typed and Array-of-STRUCT typed fields and these can be
// specified using named struct-typed and []struct-typed fields inside a Go struct. However,
// embedded struct fields are not allowed. Unexported struct fields are ignored.
//
// NULL STRUCT values in Cloud Spanner are typed. A nil pointer to a Go struct value can be used to
// specify a NULL STRUCT value of the corresponding spanner.StructType. Nil and empty slices of a
// Go STRUCT type can be used to specify NULL and empty array values respectively of the
// corresponding spanner.StructType. A slice of pointers to a Go struct type can be used to specify
// an array of NULL-able STRUCT values.
// String implements Stringer.String for NullFloat64
func (n NullFloat64) String() string {
if !n.Valid {
@ -1106,28 +1086,33 @@ func decodeRowArray(ty *sppb.StructType, pb *proto3.ListValue) ([]NullRow, error
return a, nil
}
// errNilSpannerStructType returns error for unexpected nil Cloud Spanner STRUCT schema type in decoding.
// errNilSpannerStructType returns error for unexpected nil Cloud Spanner STRUCT
// schema type in decoding.
func errNilSpannerStructType() error {
return spannerErrorf(codes.FailedPrecondition, "unexpected nil StructType in decoding Cloud Spanner STRUCT")
}
// errUnnamedField returns error for decoding a Cloud Spanner STRUCT with unnamed field into a Go struct.
// errUnnamedField returns error for decoding a Cloud Spanner STRUCT with
// unnamed field into a Go struct.
func errUnnamedField(ty *sppb.StructType, i int) error {
return spannerErrorf(codes.InvalidArgument, "unnamed field %v in Cloud Spanner STRUCT %+v", i, ty)
}
// errNoOrDupGoField returns error for decoding a Cloud Spanner
// STRUCT into a Go struct which is either missing a field, or has duplicate fields.
// STRUCT into a Go struct which is either missing a field, or has duplicate
// fields.
func errNoOrDupGoField(s interface{}, f string) error {
return spannerErrorf(codes.InvalidArgument, "Go struct %+v(type %T) has no or duplicate fields for Cloud Spanner STRUCT field %v", s, s, f)
}
// errDupColNames returns error for duplicated Cloud Spanner STRUCT field names found in decoding a Cloud Spanner STRUCT into a Go struct.
// errDupColNames returns error for duplicated Cloud Spanner STRUCT field names
// found in decoding a Cloud Spanner STRUCT into a Go struct.
func errDupSpannerField(f string, ty *sppb.StructType) error {
return spannerErrorf(codes.InvalidArgument, "duplicated field name %q in Cloud Spanner STRUCT %+v", f, ty)
}
// errDecodeStructField returns error for failure in decoding a single field of a Cloud Spanner STRUCT.
// errDecodeStructField returns error for failure in decoding a single field of
// a Cloud Spanner STRUCT.
func errDecodeStructField(ty *sppb.StructType, f string, err error) error {
se, ok := toSpannerError(err).(*Error)
if !ok {
@ -1138,7 +1123,8 @@ func errDecodeStructField(ty *sppb.StructType, f string, err error) error {
return se
}
// decodeStruct decodes proto3.ListValue pb into struct referenced by pointer ptr, according to
// decodeStruct decodes proto3.ListValue pb into struct referenced by pointer
// ptr, according to
// the structural information given in sppb.StructType ty.
func decodeStruct(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error {
if reflect.ValueOf(ptr).IsNil() {
@ -1192,7 +1178,8 @@ func isPtrStructPtrSlice(t reflect.Type) bool {
return true
}
// decodeStructArray decodes proto3.ListValue pb into struct slice referenced by pointer ptr, according to the
// decodeStructArray decodes proto3.ListValue pb into struct slice referenced by
// pointer ptr, according to the
// structural information given in a sppb.StructType.
func decodeStructArray(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error {
if pb == nil {
@ -1229,8 +1216,8 @@ func decodeStructArray(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{
return nil
}
// errEncoderUnsupportedType returns error for not being able to encode a value of
// certain type.
// errEncoderUnsupportedType returns error for not being able to encode a value
// of certain type.
func errEncoderUnsupportedType(v interface{}) error {
return spannerErrorf(codes.InvalidArgument, "client doesn't support type %T", v)
}
@ -1443,8 +1430,8 @@ func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
return pb, pt, nil
}
// Encodes a Go struct value/ptr in v to the spanner Value and Type protos. v itself must
// be non-nil.
// Encodes a Go struct value/ptr in v to the spanner Value and Type protos. v
// itself must be non-nil.
func encodeStruct(v interface{}) (*proto3.Value, *sppb.Type, error) {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
@ -1453,8 +1440,8 @@ func encodeStruct(v interface{}) (*proto3.Value, *sppb.Type, error) {
if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct {
typ = typ.Elem()
if val.IsNil() {
// nil pointer to struct, representing a NULL STRUCT value. Use a dummy value to
// get the type.
// nil pointer to struct, representing a NULL STRUCT value. Use a
// dummy value to get the type.
_, st, err := encodeStruct(reflect.Zero(typ).Interface())
if err != nil {
return nil, nil, err
@ -1504,8 +1491,8 @@ func encodeStruct(v interface{}) (*proto3.Value, *sppb.Type, error) {
return listProto(stv...), structType(stf...), nil
}
// Encodes a slice of Go struct values/ptrs in v to the spanner Value and Type protos. v itself
// must be non-nil.
// Encodes a slice of Go struct values/ptrs in v to the spanner Value and Type
// protos. v itself must be non-nil.
func encodeStructArray(v interface{}) (*proto3.Value, *sppb.Type, error) {
etyp := reflect.TypeOf(v).Elem()
sliceval := reflect.ValueOf(v)
@ -1515,7 +1502,7 @@ func encodeStructArray(v interface{}) (*proto3.Value, *sppb.Type, error) {
etyp = etyp.Elem()
}
// Use a dummy struct value to get the element type
// Use a dummy struct value to get the element type.
_, elemTyp, err := encodeStruct(reflect.Zero(etyp).Interface())
if err != nil {
return nil, nil, err
@ -1582,7 +1569,8 @@ func encodeValueArray(vs []interface{}) (*proto3.ListValue, error) {
return lv, nil
}
// encodeArray assumes that all values of the array element type encode without error.
// encodeArray assumes that all values of the array element type encode without
// error.
func encodeArray(len int, at func(int) interface{}) (*proto3.Value, error) {
vs := make([]*proto3.Value, len)
var err error

View file

@ -23,6 +23,7 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
@ -118,6 +119,20 @@ func (c *Client) Close() error {
return nil
}
// SigningScheme determines the API version to use when signing URLs.
type SigningScheme int
const (
// SigningSchemeDefault is presently V2 and will change to V4 in the future.
SigningSchemeDefault SigningScheme = iota
// SigningSchemeV2 uses the V2 scheme to sign URLs.
SigningSchemeV2
// SigningSchemeV4 uses the V4 scheme to sign URLs.
SigningSchemeV4
)
// SignedURLOptions allows you to restrict the access to the signed URL.
type SignedURLOptions struct {
// GoogleAccessID represents the authorizer of the signed URL generation.
@ -140,8 +155,9 @@ type SignedURLOptions struct {
// Exactly one of PrivateKey or SignBytes must be non-nil.
PrivateKey []byte
// SignBytes is a function for implementing custom signing.
// If your application is running on Google App Engine, you can use appengine's internal signing function:
// SignBytes is a function for implementing custom signing. For example, if
// your application is running on Google App Engine, you can use
// appengine's internal signing function:
// ctx := appengine.NewContext(request)
// acc, _ := appengine.ServiceAccount(ctx)
// url, err := SignedURL("bucket", "object", &SignedURLOptions{
@ -162,7 +178,8 @@ type SignedURLOptions struct {
Method string
// Expires is the expiration time on the signed URL. It must be
// a datetime in the future.
// a datetime in the future. For SigningSchemeV4, the expiration may be no
// more than seven days in the future.
// Required.
Expires time.Time
@ -181,9 +198,17 @@ type SignedURLOptions struct {
// header in order to use the signed URL.
// Optional.
MD5 string
// Scheme determines the version of URL signing to use. Default is
// SigningSchemeV2.
Scheme SigningScheme
}
var (
tabRegex = regexp.MustCompile(`[\t]+`)
// I was tempted to call this spacex. :)
spaceRegex = regexp.MustCompile(` +`)
canonicalHeaderRegexp = regexp.MustCompile(`(?i)^(x-goog-[^:]+):(.*)?$`)
excludedCanonicalHeaders = map[string]bool{
"x-goog-encryption-key": true,
@ -191,26 +216,31 @@ var (
}
)
// sanitizeHeaders applies the specifications for canonical extension headers at
// v2SanitizeHeaders applies the specifications for canonical extension headers at
// https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers.
func sanitizeHeaders(hdrs []string) []string {
func v2SanitizeHeaders(hdrs []string) []string {
headerMap := map[string][]string{}
for _, hdr := range hdrs {
// No leading or trailing whitespaces.
sanitizedHeader := strings.TrimSpace(hdr)
var header, value string
// Only keep canonical headers, discard any others.
headerMatches := canonicalHeaderRegexp.FindStringSubmatch(sanitizedHeader)
if len(headerMatches) == 0 {
continue
}
header = headerMatches[1]
value = headerMatches[2]
header := strings.ToLower(strings.TrimSpace(headerMatches[1]))
if excludedCanonicalHeaders[headerMatches[1]] {
header = strings.ToLower(strings.TrimSpace(header))
value = strings.TrimSpace(value)
if excludedCanonicalHeaders[header] {
// Do not keep any deliberately excluded canonical headers when signing.
continue
}
value := strings.TrimSpace(headerMatches[2])
if len(value) > 0 {
// Remove duplicate headers by appending the values of duplicates
// in their order of appearance.
@ -220,51 +250,256 @@ func sanitizeHeaders(hdrs []string) []string {
var sanitizedHeaders []string
for header, values := range headerMap {
// There should be no spaces around the colon separating the
// header name from the header value or around the values
// themselves. The values should be separated by commas.
// There should be no spaces around the colon separating the header name
// from the header value or around the values themselves. The values
// should be separated by commas.
//
// NOTE: The semantics for headers without a value are not clear.
// However from specifications these should be edge-cases
// anyway and we should assume that there will be no
// canonical headers using empty values. Any such headers
// are discarded at the regexp stage above.
sanitizedHeaders = append(
sanitizedHeaders,
fmt.Sprintf("%s:%s", header, strings.Join(values, ",")),
)
// However from specifications these should be edge-cases anyway and we
// should assume that there will be no canonical headers using empty
// values. Any such headers are discarded at the regexp stage above.
sanitizedHeaders = append(sanitizedHeaders, fmt.Sprintf("%s:%s", header, strings.Join(values, ",")))
}
sort.Strings(sanitizedHeaders)
return sanitizedHeaders
}
// v4SanitizeHeaders applies the specifications for canonical extension headers
// at https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers.
//
// V4 does a couple things differently from V2:
// - Headers get sorted by key, instead of by key:value. We do this in
// signedURLV4.
// - There's no canonical regexp: we simply split headers on :.
// - We don't exclude canonical headers.
// - We replace leading and trailing spaces in header values, like v2, but also
// all intermediate space duplicates get stripped. That is, there's only ever
// a single consecutive space.
func v4SanitizeHeaders(hdrs []string) []string {
headerMap := map[string][]string{}
for _, hdr := range hdrs {
// No leading or trailing whitespaces.
sanitizedHeader := strings.TrimSpace(hdr)
var key, value string
headerMatches := strings.Split(sanitizedHeader, ":")
if len(headerMatches) < 2 {
continue
}
key = headerMatches[0]
value = headerMatches[1]
key = strings.ToLower(strings.TrimSpace(key))
value = strings.TrimSpace(value)
value = string(spaceRegex.ReplaceAll([]byte(value), []byte(" ")))
value = string(tabRegex.ReplaceAll([]byte(value), []byte("\t")))
if len(value) > 0 {
// Remove duplicate headers by appending the values of duplicates
// in their order of appearance.
headerMap[key] = append(headerMap[key], value)
}
}
var sanitizedHeaders []string
for header, values := range headerMap {
// There should be no spaces around the colon separating the header name
// from the header value or around the values themselves. The values
// should be separated by commas.
//
// NOTE: The semantics for headers without a value are not clear.
// However from specifications these should be edge-cases anyway and we
// should assume that there will be no canonical headers using empty
// values. Any such headers are discarded at the regexp stage above.
sanitizedHeaders = append(sanitizedHeaders, fmt.Sprintf("%s:%s", header, strings.Join(values, ",")))
}
return sanitizedHeaders
}
// SignedURL returns a URL for the specified object. Signed URLs allow
// the users access to a restricted resource for a limited time without having a
// Google account or signing in. For more information about the signed
// URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
now := utcNow()
if err := validateOptions(opts, now); err != nil {
return "", err
}
switch opts.Scheme {
case SigningSchemeV2:
opts.Headers = v2SanitizeHeaders(opts.Headers)
return signedURLV2(bucket, name, opts)
case SigningSchemeV4:
opts.Headers = v4SanitizeHeaders(opts.Headers)
return signedURLV4(bucket, name, opts, now)
default: // SigningSchemeDefault
opts.Headers = v2SanitizeHeaders(opts.Headers)
return signedURLV2(bucket, name, opts)
}
}
func validateOptions(opts *SignedURLOptions, now time.Time) error {
if opts == nil {
return "", errors.New("storage: missing required SignedURLOptions")
return errors.New("storage: missing required SignedURLOptions")
}
if opts.GoogleAccessID == "" {
return "", errors.New("storage: missing required GoogleAccessID")
return errors.New("storage: missing required GoogleAccessID")
}
if (opts.PrivateKey == nil) == (opts.SignBytes == nil) {
return "", errors.New("storage: exactly one of PrivateKey or SignedBytes must be set")
return errors.New("storage: exactly one of PrivateKey or SignedBytes must be set")
}
if opts.Method == "" {
return "", errors.New("storage: missing required method option")
return errors.New("storage: missing required method option")
}
if opts.Expires.IsZero() {
return "", errors.New("storage: missing required expires option")
return errors.New("storage: missing required expires option")
}
if opts.MD5 != "" {
md5, err := base64.StdEncoding.DecodeString(opts.MD5)
if err != nil || len(md5) != 16 {
return "", errors.New("storage: invalid MD5 checksum")
return errors.New("storage: invalid MD5 checksum")
}
}
opts.Headers = sanitizeHeaders(opts.Headers)
if opts.Scheme == SigningSchemeV4 {
cutoff := now.Add(604801 * time.Second) // 7 days + 1 second
if !opts.Expires.Before(cutoff) {
return errors.New("storage: expires must be within seven days from now")
}
}
return nil
}
const (
iso8601 = "20060102T150405Z"
yearMonthDay = "20060102"
)
// utcNow returns the current time in UTC and is a variable to allow for
// reassignment in tests to provide deterministic signed URL values.
var utcNow = func() time.Time {
return time.Now().UTC()
}
// extractHeaderNames takes in a series of key:value headers and returns the
// header names only.
func extractHeaderNames(kvs []string) []string {
var res []string
for _, header := range kvs {
nameValue := strings.Split(header, ":")
res = append(res, nameValue[0])
}
return res
}
// signedURLV4 creates a signed URL using the sigV4 algorithm.
func signedURLV4(bucket, name string, opts *SignedURLOptions, now time.Time) (string, error) {
buf := &bytes.Buffer{}
fmt.Fprintf(buf, "%s\n", opts.Method)
u := &url.URL{Path: bucket}
if name != "" {
u.Path += "/" + name
}
// Note: we have to add a / here because GCS does so auto-magically, despite
// Go's EscapedPath not doing so (and we have to exactly match their
// canonical query).
fmt.Fprintf(buf, "/%s\n", u.EscapedPath())
headerNames := append(extractHeaderNames(opts.Headers), "host")
if opts.ContentType != "" {
headerNames = append(headerNames, "content-type")
}
if opts.MD5 != "" {
headerNames = append(headerNames, "content-md5")
}
sort.Strings(headerNames)
signedHeaders := strings.Join(headerNames, ";")
timestamp := now.Format(iso8601)
credentialScope := fmt.Sprintf("%s/auto/storage/goog4_request", now.Format(yearMonthDay))
canonicalQueryString := url.Values{
"X-Goog-Algorithm": {"GOOG4-RSA-SHA256"},
"X-Goog-Credential": {fmt.Sprintf("%s/%s", opts.GoogleAccessID, credentialScope)},
"X-Goog-Date": {timestamp},
"X-Goog-Expires": {fmt.Sprintf("%d", int(opts.Expires.Sub(now).Seconds()))},
"X-Goog-SignedHeaders": {signedHeaders},
}
fmt.Fprintf(buf, "%s\n", canonicalQueryString.Encode())
u.Host = "storage.googleapis.com"
var headersWithValue []string
headersWithValue = append(headersWithValue, "host:"+u.Host)
headersWithValue = append(headersWithValue, opts.Headers...)
if opts.ContentType != "" {
headersWithValue = append(headersWithValue, "content-type:"+strings.TrimSpace(opts.ContentType))
}
if opts.MD5 != "" {
headersWithValue = append(headersWithValue, "content-md5:"+strings.TrimSpace(opts.MD5))
}
canonicalHeaders := strings.Join(sortHeadersByKey(headersWithValue), "\n")
fmt.Fprintf(buf, "%s\n\n", canonicalHeaders)
fmt.Fprintf(buf, "%s\n", signedHeaders)
fmt.Fprint(buf, "UNSIGNED-PAYLOAD")
sum := sha256.Sum256(buf.Bytes())
hexDigest := hex.EncodeToString(sum[:])
signBuf := &bytes.Buffer{}
fmt.Fprint(signBuf, "GOOG4-RSA-SHA256\n")
fmt.Fprintf(signBuf, "%s\n", timestamp)
fmt.Fprintf(signBuf, "%s\n", credentialScope)
fmt.Fprintf(signBuf, "%s", hexDigest)
signBytes := opts.SignBytes
if opts.PrivateKey != nil {
key, err := parseKey(opts.PrivateKey)
if err != nil {
return "", err
}
signBytes = func(b []byte) ([]byte, error) {
sum := sha256.Sum256(b)
return rsa.SignPKCS1v15(
rand.Reader,
key,
crypto.SHA256,
sum[:],
)
}
}
b, err := signBytes(signBuf.Bytes())
if err != nil {
return "", err
}
signature := hex.EncodeToString(b)
canonicalQueryString.Set("X-Goog-Signature", string(signature))
u.Scheme = "https"
u.RawQuery = canonicalQueryString.Encode()
return u.String(), nil
}
// takes a list of headerKey:headervalue1,headervalue2,etc and sorts by header
// key.
func sortHeadersByKey(hdrs []string) []string {
headersMap := map[string]string{}
var headersKeys []string
for _, h := range hdrs {
parts := strings.Split(h, ":")
k := parts[0]
v := parts[1]
headersMap[k] = v
headersKeys = append(headersKeys, k)
}
sort.Strings(headersKeys)
var sorted []string
for _, k := range headersKeys {
v := headersMap[k]
sorted = append(sorted, fmt.Sprintf("%s:%s", k, v))
}
return sorted
}
func signedURLV2(bucket, name string, opts *SignedURLOptions) (string, error) {
signBytes := opts.SignBytes
if opts.PrivateKey != nil {
key, err := parseKey(opts.PrivateKey)

File diff suppressed because one or more lines are too long

View file

@ -273,6 +273,7 @@ func (client VirtualMachinesClient) CreateOrUpdatePreparer(ctx context.Context,
"api-version": APIVersion,
}
parameters.Resources = nil
preparer := autorest.CreatePreparer(
autorest.AsContentType("application/json; charset=utf-8"),
autorest.AsPut(),

View file

@ -1108,6 +1108,9 @@ func (client VirtualMachineScaleSetVMsClient) UpdatePreparer(ctx context.Context
"api-version": APIVersion,
}
parameters.InstanceID = nil
parameters.Sku = nil
parameters.Resources = nil
preparer := autorest.CreatePreparer(
autorest.AsContentType("application/json; charset=utf-8"),
autorest.AsPut(),

View file

@ -152,9 +152,9 @@ type ADGroup struct {
Mail *string `json:"mail,omitempty"`
// AdditionalProperties - Unmatched properties from the message are deserialized this collection
AdditionalProperties map[string]interface{} `json:""`
// ObjectID - The object ID.
// ObjectID - READ-ONLY; The object ID.
ObjectID *string `json:"objectId,omitempty"`
// DeletionTimestamp - The time at which the directory object was deleted.
// DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted.
DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"`
// ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser'
ObjectType ObjectType `json:"objectType,omitempty"`
@ -179,12 +179,6 @@ func (ag ADGroup) MarshalJSON() ([]byte, error) {
if ag.Mail != nil {
objectMap["mail"] = ag.Mail
}
if ag.ObjectID != nil {
objectMap["objectId"] = ag.ObjectID
}
if ag.DeletionTimestamp != nil {
objectMap["deletionTimestamp"] = ag.DeletionTimestamp
}
if ag.ObjectType != "" {
objectMap["objectType"] = ag.ObjectType
}
@ -391,9 +385,9 @@ type Application struct {
WwwHomepage *string `json:"wwwHomepage,omitempty"`
// AdditionalProperties - Unmatched properties from the message are deserialized this collection
AdditionalProperties map[string]interface{} `json:""`
// ObjectID - The object ID.
// ObjectID - READ-ONLY; The object ID.
ObjectID *string `json:"objectId,omitempty"`
// DeletionTimestamp - The time at which the directory object was deleted.
// DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted.
DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"`
// ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser'
ObjectType ObjectType `json:"objectType,omitempty"`
@ -499,12 +493,6 @@ func (a Application) MarshalJSON() ([]byte, error) {
if a.WwwHomepage != nil {
objectMap["wwwHomepage"] = a.WwwHomepage
}
if a.ObjectID != nil {
objectMap["objectId"] = a.ObjectID
}
if a.DeletionTimestamp != nil {
objectMap["deletionTimestamp"] = a.DeletionTimestamp
}
if a.ObjectType != "" {
objectMap["objectType"] = a.ObjectType
}
@ -1367,9 +1355,9 @@ type BasicDirectoryObject interface {
type DirectoryObject struct {
// AdditionalProperties - Unmatched properties from the message are deserialized this collection
AdditionalProperties map[string]interface{} `json:""`
// ObjectID - The object ID.
// ObjectID - READ-ONLY; The object ID.
ObjectID *string `json:"objectId,omitempty"`
// DeletionTimestamp - The time at which the directory object was deleted.
// DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted.
DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"`
// ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser'
ObjectType ObjectType `json:"objectType,omitempty"`
@ -1428,12 +1416,6 @@ func unmarshalBasicDirectoryObjectArray(body []byte) ([]BasicDirectoryObject, er
func (do DirectoryObject) MarshalJSON() ([]byte, error) {
do.ObjectType = ObjectTypeDirectoryObject
objectMap := make(map[string]interface{})
if do.ObjectID != nil {
objectMap["objectId"] = do.ObjectID
}
if do.DeletionTimestamp != nil {
objectMap["deletionTimestamp"] = do.DeletionTimestamp
}
if do.ObjectType != "" {
objectMap["objectType"] = do.ObjectType
}
@ -1710,11 +1692,11 @@ type Domain struct {
autorest.Response `json:"-"`
// AdditionalProperties - Unmatched properties from the message are deserialized this collection
AdditionalProperties map[string]interface{} `json:""`
// AuthenticationType - the type of the authentication into the domain.
// AuthenticationType - READ-ONLY; the type of the authentication into the domain.
AuthenticationType *string `json:"authenticationType,omitempty"`
// IsDefault - if this is the default domain in the tenant.
// IsDefault - READ-ONLY; if this is the default domain in the tenant.
IsDefault *bool `json:"isDefault,omitempty"`
// IsVerified - if this domain's ownership is verified.
// IsVerified - READ-ONLY; if this domain's ownership is verified.
IsVerified *bool `json:"isVerified,omitempty"`
// Name - the domain name.
Name *string `json:"name,omitempty"`
@ -1723,15 +1705,6 @@ type Domain struct {
// MarshalJSON is the custom marshaler for Domain.
func (d Domain) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if d.AuthenticationType != nil {
objectMap["authenticationType"] = d.AuthenticationType
}
if d.IsDefault != nil {
objectMap["isDefault"] = d.IsDefault
}
if d.IsVerified != nil {
objectMap["isVerified"] = d.IsVerified
}
if d.Name != nil {
objectMap["name"] = d.Name
}
@ -3081,13 +3054,14 @@ func (ra *ResourceAccess) UnmarshalJSON(body []byte) error {
type ServicePrincipal struct {
autorest.Response `json:"-"`
// AccountEnabled - whether or not the service principal account is enabled
AccountEnabled *string `json:"accountEnabled,omitempty"`
AccountEnabled *bool `json:"accountEnabled,omitempty"`
// AlternativeNames - alternative names
AlternativeNames *[]string `json:"alternativeNames,omitempty"`
// AppDisplayName - The display name exposed by the associated application.
// AppDisplayName - READ-ONLY; The display name exposed by the associated application.
AppDisplayName *string `json:"appDisplayName,omitempty"`
// AppID - The application ID.
AppID *string `json:"appId,omitempty"`
AppID *string `json:"appId,omitempty"`
// AppOwnerTenantID - READ-ONLY
AppOwnerTenantID *string `json:"appOwnerTenantId,omitempty"`
// AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application.
AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"`
@ -3103,7 +3077,7 @@ type ServicePrincipal struct {
KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"`
// LogoutURL - A URL provided by the author of the associated application to logout
LogoutURL *string `json:"logoutUrl,omitempty"`
// Oauth2Permissions - The OAuth 2.0 permissions exposed by the associated application.
// Oauth2Permissions - READ-ONLY; The OAuth 2.0 permissions exposed by the associated application.
Oauth2Permissions *[]OAuth2Permission `json:"oauth2Permissions,omitempty"`
// PasswordCredentials - The collection of password credentials associated with the service principal.
PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"`
@ -3123,9 +3097,9 @@ type ServicePrincipal struct {
Tags *[]string `json:"tags,omitempty"`
// AdditionalProperties - Unmatched properties from the message are deserialized this collection
AdditionalProperties map[string]interface{} `json:""`
// ObjectID - The object ID.
// ObjectID - READ-ONLY; The object ID.
ObjectID *string `json:"objectId,omitempty"`
// DeletionTimestamp - The time at which the directory object was deleted.
// DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted.
DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"`
// ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser'
ObjectType ObjectType `json:"objectType,omitempty"`
@ -3141,15 +3115,9 @@ func (sp ServicePrincipal) MarshalJSON() ([]byte, error) {
if sp.AlternativeNames != nil {
objectMap["alternativeNames"] = sp.AlternativeNames
}
if sp.AppDisplayName != nil {
objectMap["appDisplayName"] = sp.AppDisplayName
}
if sp.AppID != nil {
objectMap["appId"] = sp.AppID
}
if sp.AppOwnerTenantID != nil {
objectMap["appOwnerTenantId"] = sp.AppOwnerTenantID
}
if sp.AppRoleAssignmentRequired != nil {
objectMap["appRoleAssignmentRequired"] = sp.AppRoleAssignmentRequired
}
@ -3171,9 +3139,6 @@ func (sp ServicePrincipal) MarshalJSON() ([]byte, error) {
if sp.LogoutURL != nil {
objectMap["logoutUrl"] = sp.LogoutURL
}
if sp.Oauth2Permissions != nil {
objectMap["oauth2Permissions"] = sp.Oauth2Permissions
}
if sp.PasswordCredentials != nil {
objectMap["passwordCredentials"] = sp.PasswordCredentials
}
@ -3198,12 +3163,6 @@ func (sp ServicePrincipal) MarshalJSON() ([]byte, error) {
if sp.Tags != nil {
objectMap["tags"] = sp.Tags
}
if sp.ObjectID != nil {
objectMap["objectId"] = sp.ObjectID
}
if sp.DeletionTimestamp != nil {
objectMap["deletionTimestamp"] = sp.DeletionTimestamp
}
if sp.ObjectType != "" {
objectMap["objectType"] = sp.ObjectType
}
@ -3254,7 +3213,7 @@ func (sp *ServicePrincipal) UnmarshalJSON(body []byte) error {
switch k {
case "accountEnabled":
if v != nil {
var accountEnabled string
var accountEnabled bool
err = json.Unmarshal(*v, &accountEnabled)
if err != nil {
return err
@ -3490,7 +3449,7 @@ func (sp *ServicePrincipal) UnmarshalJSON(body []byte) error {
// PATCH
type ServicePrincipalBase struct {
// AccountEnabled - whether or not the service principal account is enabled
AccountEnabled *string `json:"accountEnabled,omitempty"`
AccountEnabled *bool `json:"accountEnabled,omitempty"`
// AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application.
AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"`
// KeyCredentials - The collection of key credentials associated with the service principal.
@ -3508,7 +3467,7 @@ type ServicePrincipalCreateParameters struct {
// AppID - The application ID.
AppID *string `json:"appId,omitempty"`
// AccountEnabled - whether or not the service principal account is enabled
AccountEnabled *string `json:"accountEnabled,omitempty"`
AccountEnabled *bool `json:"accountEnabled,omitempty"`
// AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application.
AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"`
// KeyCredentials - The collection of key credentials associated with the service principal.
@ -3667,7 +3626,7 @@ type ServicePrincipalObjectResult struct {
// ServicePrincipalUpdateParameters request parameters for update an existing service principal.
type ServicePrincipalUpdateParameters struct {
// AccountEnabled - whether or not the service principal account is enabled
AccountEnabled *string `json:"accountEnabled,omitempty"`
AccountEnabled *bool `json:"accountEnabled,omitempty"`
// AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application.
AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"`
// KeyCredentials - The collection of key credentials associated with the service principal.
@ -3778,9 +3737,9 @@ type User struct {
SignInNames *[]SignInName `json:"signInNames,omitempty"`
// AdditionalProperties - Unmatched properties from the message are deserialized this collection
AdditionalProperties map[string]interface{} `json:""`
// ObjectID - The object ID.
// ObjectID - READ-ONLY; The object ID.
ObjectID *string `json:"objectId,omitempty"`
// DeletionTimestamp - The time at which the directory object was deleted.
// DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted.
DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"`
// ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser'
ObjectType ObjectType `json:"objectType,omitempty"`
@ -3823,12 +3782,6 @@ func (u User) MarshalJSON() ([]byte, error) {
if u.SignInNames != nil {
objectMap["signInNames"] = u.SignInNames
}
if u.ObjectID != nil {
objectMap["objectId"] = u.ObjectID
}
if u.DeletionTimestamp != nil {
objectMap["deletionTimestamp"] = u.DeletionTimestamp
}
if u.ObjectType != "" {
objectMap["objectType"] = u.ObjectType
}

View file

@ -4848,6 +4848,7 @@ func (client BaseClient) SetCertificateContactsPreparer(ctx context.Context, vau
"api-version": APIVersion,
}
contacts.ID = nil
preparer := autorest.CreatePreparer(
autorest.AsContentType("application/json; charset=utf-8"),
autorest.AsPut(),
@ -5733,6 +5734,7 @@ func (client BaseClient) UpdateCertificatePolicyPreparer(ctx context.Context, va
"api-version": APIVersion,
}
certificatePolicy.ID = nil
preparer := autorest.CreatePreparer(
autorest.AsContentType("application/json; charset=utf-8"),
autorest.AsPatch(),

View file

@ -236,29 +236,29 @@ type Attributes struct {
NotBefore *date.UnixTime `json:"nbf,omitempty"`
// Expires - Expiry date in UTC.
Expires *date.UnixTime `json:"exp,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
// BackupKeyResult the backup key result, containing the backup blob.
type BackupKeyResult struct {
autorest.Response `json:"-"`
// Value - The backup blob containing the backed up key. (a URL-encoded base64 string)
// Value - READ-ONLY; The backup blob containing the backed up key. (a URL-encoded base64 string)
Value *string `json:"value,omitempty"`
}
// BackupSecretResult the backup secret result, containing the backup blob.
type BackupSecretResult struct {
autorest.Response `json:"-"`
// Value - The backup blob containing the backed up secret. (a URL-encoded base64 string)
// Value - READ-ONLY; The backup blob containing the backed up secret. (a URL-encoded base64 string)
Value *string `json:"value,omitempty"`
}
// CertificateAttributes the certificate management attributes.
type CertificateAttributes struct {
// RecoveryLevel - Reflects the deletion recovery level currently in effect for certificates in the current vault. If it contains 'Purgeable', the certificate can be permanently deleted by a privileged user; otherwise, only the system can purge the certificate, at the end of the retention interval. Possible values include: 'Purgeable', 'RecoverablePurgeable', 'Recoverable', 'RecoverableProtectedSubscription'
// RecoveryLevel - READ-ONLY; Reflects the deletion recovery level currently in effect for certificates in the current vault. If it contains 'Purgeable', the certificate can be permanently deleted by a privileged user; otherwise, only the system can purge the certificate, at the end of the retention interval. Possible values include: 'Purgeable', 'RecoverablePurgeable', 'Recoverable', 'RecoverableProtectedSubscription'
RecoveryLevel DeletionRecoveryLevel `json:"recoveryLevel,omitempty"`
// Enabled - Determines whether the object is enabled.
Enabled *bool `json:"enabled,omitempty"`
@ -266,24 +266,24 @@ type CertificateAttributes struct {
NotBefore *date.UnixTime `json:"nbf,omitempty"`
// Expires - Expiry date in UTC.
Expires *date.UnixTime `json:"exp,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
// CertificateBundle a certificate bundle consists of a certificate (X509) plus its attributes.
type CertificateBundle struct {
autorest.Response `json:"-"`
// ID - The certificate id.
// ID - READ-ONLY; The certificate id.
ID *string `json:"id,omitempty"`
// Kid - The key id.
// Kid - READ-ONLY; The key id.
Kid *string `json:"kid,omitempty"`
// Sid - The secret id.
// Sid - READ-ONLY; The secret id.
Sid *string `json:"sid,omitempty"`
// X509Thumbprint - Thumbprint of the certificate. (a URL-encoded base64 string)
// X509Thumbprint - READ-ONLY; Thumbprint of the certificate. (a URL-encoded base64 string)
X509Thumbprint *string `json:"x5t,omitempty"`
// Policy - The management policy.
// Policy - READ-ONLY; The management policy.
Policy *CertificatePolicy `json:"policy,omitempty"`
// Cer - CER contents of x509 certificate.
Cer *[]byte `json:"cer,omitempty"`
@ -298,21 +298,6 @@ type CertificateBundle struct {
// MarshalJSON is the custom marshaler for CertificateBundle.
func (cb CertificateBundle) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if cb.ID != nil {
objectMap["id"] = cb.ID
}
if cb.Kid != nil {
objectMap["kid"] = cb.Kid
}
if cb.Sid != nil {
objectMap["sid"] = cb.Sid
}
if cb.X509Thumbprint != nil {
objectMap["x5t"] = cb.X509Thumbprint
}
if cb.Policy != nil {
objectMap["policy"] = cb.Policy
}
if cb.Cer != nil {
objectMap["cer"] = cb.Cer
}
@ -399,9 +384,9 @@ type CertificateIssuerItem struct {
// CertificateIssuerListResult the certificate issuer list result.
type CertificateIssuerListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of certificate issuers in the key vault along with a link to the next page of certificate issuers.
// Value - READ-ONLY; A response message containing a list of certificate issuers in the key vault along with a link to the next page of certificate issuers.
Value *[]CertificateIssuerItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of certificate issuers.
// NextLink - READ-ONLY; The URL to get the next set of certificate issuers.
NextLink *string `json:"nextLink,omitempty"`
}
@ -600,9 +585,9 @@ func (ci CertificateItem) MarshalJSON() ([]byte, error) {
// CertificateListResult the certificate list result.
type CertificateListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of certificates in the key vault along with a link to the next page of certificates.
// Value - READ-ONLY; A response message containing a list of certificates in the key vault along with a link to the next page of certificates.
Value *[]CertificateItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of certificates.
// NextLink - READ-ONLY; The URL to get the next set of certificates.
NextLink *string `json:"nextLink,omitempty"`
}
@ -771,7 +756,7 @@ func (cmp CertificateMergeParameters) MarshalJSON() ([]byte, error) {
// CertificateOperation a certificate operation is returned in case of asynchronous requests.
type CertificateOperation struct {
autorest.Response `json:"-"`
// ID - The certificate id.
// ID - READ-ONLY; The certificate id.
ID *string `json:"id,omitempty"`
// IssuerParameters - Parameters for the issuer of the X509 component of a certificate.
IssuerParameters *IssuerParameters `json:"issuer,omitempty"`
@ -800,7 +785,7 @@ type CertificateOperationUpdateParameter struct {
// CertificatePolicy management policy for a certificate.
type CertificatePolicy struct {
autorest.Response `json:"-"`
// ID - The certificate id.
// ID - READ-ONLY; The certificate id.
ID *string `json:"id,omitempty"`
// KeyProperties - Properties of the key backing a certificate.
KeyProperties *KeyProperties `json:"key_props,omitempty"`
@ -854,7 +839,7 @@ type Contact struct {
// Contacts the contacts for the vault certificates.
type Contacts struct {
autorest.Response `json:"-"`
// ID - Identifier for the contacts collection.
// ID - READ-ONLY; Identifier for the contacts collection.
ID *string `json:"id,omitempty"`
// ContactList - The contact list for the vault certificates.
ContactList *[]Contact `json:"contacts,omitempty"`
@ -866,19 +851,19 @@ type DeletedCertificateBundle struct {
autorest.Response `json:"-"`
// RecoveryID - The url of the recovery object, used to identify and recover the deleted certificate.
RecoveryID *string `json:"recoveryId,omitempty"`
// ScheduledPurgeDate - The time when the certificate is scheduled to be purged, in UTC
// ScheduledPurgeDate - READ-ONLY; The time when the certificate is scheduled to be purged, in UTC
ScheduledPurgeDate *date.UnixTime `json:"scheduledPurgeDate,omitempty"`
// DeletedDate - The time when the certificate was deleted, in UTC
// DeletedDate - READ-ONLY; The time when the certificate was deleted, in UTC
DeletedDate *date.UnixTime `json:"deletedDate,omitempty"`
// ID - The certificate id.
// ID - READ-ONLY; The certificate id.
ID *string `json:"id,omitempty"`
// Kid - The key id.
// Kid - READ-ONLY; The key id.
Kid *string `json:"kid,omitempty"`
// Sid - The secret id.
// Sid - READ-ONLY; The secret id.
Sid *string `json:"sid,omitempty"`
// X509Thumbprint - Thumbprint of the certificate. (a URL-encoded base64 string)
// X509Thumbprint - READ-ONLY; Thumbprint of the certificate. (a URL-encoded base64 string)
X509Thumbprint *string `json:"x5t,omitempty"`
// Policy - The management policy.
// Policy - READ-ONLY; The management policy.
Policy *CertificatePolicy `json:"policy,omitempty"`
// Cer - CER contents of x509 certificate.
Cer *[]byte `json:"cer,omitempty"`
@ -896,27 +881,6 @@ func (dcb DeletedCertificateBundle) MarshalJSON() ([]byte, error) {
if dcb.RecoveryID != nil {
objectMap["recoveryId"] = dcb.RecoveryID
}
if dcb.ScheduledPurgeDate != nil {
objectMap["scheduledPurgeDate"] = dcb.ScheduledPurgeDate
}
if dcb.DeletedDate != nil {
objectMap["deletedDate"] = dcb.DeletedDate
}
if dcb.ID != nil {
objectMap["id"] = dcb.ID
}
if dcb.Kid != nil {
objectMap["kid"] = dcb.Kid
}
if dcb.Sid != nil {
objectMap["sid"] = dcb.Sid
}
if dcb.X509Thumbprint != nil {
objectMap["x5t"] = dcb.X509Thumbprint
}
if dcb.Policy != nil {
objectMap["policy"] = dcb.Policy
}
if dcb.Cer != nil {
objectMap["cer"] = dcb.Cer
}
@ -936,9 +900,9 @@ func (dcb DeletedCertificateBundle) MarshalJSON() ([]byte, error) {
type DeletedCertificateItem struct {
// RecoveryID - The url of the recovery object, used to identify and recover the deleted certificate.
RecoveryID *string `json:"recoveryId,omitempty"`
// ScheduledPurgeDate - The time when the certificate is scheduled to be purged, in UTC
// ScheduledPurgeDate - READ-ONLY; The time when the certificate is scheduled to be purged, in UTC
ScheduledPurgeDate *date.UnixTime `json:"scheduledPurgeDate,omitempty"`
// DeletedDate - The time when the certificate was deleted, in UTC
// DeletedDate - READ-ONLY; The time when the certificate was deleted, in UTC
DeletedDate *date.UnixTime `json:"deletedDate,omitempty"`
// ID - Certificate identifier.
ID *string `json:"id,omitempty"`
@ -956,12 +920,6 @@ func (dci DeletedCertificateItem) MarshalJSON() ([]byte, error) {
if dci.RecoveryID != nil {
objectMap["recoveryId"] = dci.RecoveryID
}
if dci.ScheduledPurgeDate != nil {
objectMap["scheduledPurgeDate"] = dci.ScheduledPurgeDate
}
if dci.DeletedDate != nil {
objectMap["deletedDate"] = dci.DeletedDate
}
if dci.ID != nil {
objectMap["id"] = dci.ID
}
@ -980,9 +938,9 @@ func (dci DeletedCertificateItem) MarshalJSON() ([]byte, error) {
// DeletedCertificateListResult a list of certificates that have been deleted in this vault.
type DeletedCertificateListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of deleted certificates in the vault along with a link to the next page of deleted certificates
// Value - READ-ONLY; A response message containing a list of deleted certificates in the vault along with a link to the next page of deleted certificates
Value *[]DeletedCertificateItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of deleted certificates.
// NextLink - READ-ONLY; The URL to get the next set of deleted certificates.
NextLink *string `json:"nextLink,omitempty"`
}
@ -1129,9 +1087,9 @@ type DeletedKeyBundle struct {
autorest.Response `json:"-"`
// RecoveryID - The url of the recovery object, used to identify and recover the deleted key.
RecoveryID *string `json:"recoveryId,omitempty"`
// ScheduledPurgeDate - The time when the key is scheduled to be purged, in UTC
// ScheduledPurgeDate - READ-ONLY; The time when the key is scheduled to be purged, in UTC
ScheduledPurgeDate *date.UnixTime `json:"scheduledPurgeDate,omitempty"`
// DeletedDate - The time when the key was deleted, in UTC
// DeletedDate - READ-ONLY; The time when the key was deleted, in UTC
DeletedDate *date.UnixTime `json:"deletedDate,omitempty"`
// Key - The Json web key.
Key *JSONWebKey `json:"key,omitempty"`
@ -1139,7 +1097,7 @@ type DeletedKeyBundle struct {
Attributes *KeyAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
// Managed - True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -1149,12 +1107,6 @@ func (dkb DeletedKeyBundle) MarshalJSON() ([]byte, error) {
if dkb.RecoveryID != nil {
objectMap["recoveryId"] = dkb.RecoveryID
}
if dkb.ScheduledPurgeDate != nil {
objectMap["scheduledPurgeDate"] = dkb.ScheduledPurgeDate
}
if dkb.DeletedDate != nil {
objectMap["deletedDate"] = dkb.DeletedDate
}
if dkb.Key != nil {
objectMap["key"] = dkb.Key
}
@ -1164,9 +1116,6 @@ func (dkb DeletedKeyBundle) MarshalJSON() ([]byte, error) {
if dkb.Tags != nil {
objectMap["tags"] = dkb.Tags
}
if dkb.Managed != nil {
objectMap["managed"] = dkb.Managed
}
return json.Marshal(objectMap)
}
@ -1174,9 +1123,9 @@ func (dkb DeletedKeyBundle) MarshalJSON() ([]byte, error) {
type DeletedKeyItem struct {
// RecoveryID - The url of the recovery object, used to identify and recover the deleted key.
RecoveryID *string `json:"recoveryId,omitempty"`
// ScheduledPurgeDate - The time when the key is scheduled to be purged, in UTC
// ScheduledPurgeDate - READ-ONLY; The time when the key is scheduled to be purged, in UTC
ScheduledPurgeDate *date.UnixTime `json:"scheduledPurgeDate,omitempty"`
// DeletedDate - The time when the key was deleted, in UTC
// DeletedDate - READ-ONLY; The time when the key was deleted, in UTC
DeletedDate *date.UnixTime `json:"deletedDate,omitempty"`
// Kid - Key identifier.
Kid *string `json:"kid,omitempty"`
@ -1184,7 +1133,7 @@ type DeletedKeyItem struct {
Attributes *KeyAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
// Managed - True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -1194,12 +1143,6 @@ func (dki DeletedKeyItem) MarshalJSON() ([]byte, error) {
if dki.RecoveryID != nil {
objectMap["recoveryId"] = dki.RecoveryID
}
if dki.ScheduledPurgeDate != nil {
objectMap["scheduledPurgeDate"] = dki.ScheduledPurgeDate
}
if dki.DeletedDate != nil {
objectMap["deletedDate"] = dki.DeletedDate
}
if dki.Kid != nil {
objectMap["kid"] = dki.Kid
}
@ -1209,18 +1152,15 @@ func (dki DeletedKeyItem) MarshalJSON() ([]byte, error) {
if dki.Tags != nil {
objectMap["tags"] = dki.Tags
}
if dki.Managed != nil {
objectMap["managed"] = dki.Managed
}
return json.Marshal(objectMap)
}
// DeletedKeyListResult a list of keys that have been deleted in this vault.
type DeletedKeyListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of deleted keys in the vault along with a link to the next page of deleted keys
// Value - READ-ONLY; A response message containing a list of deleted keys in the vault along with a link to the next page of deleted keys
Value *[]DeletedKeyItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of deleted keys.
// NextLink - READ-ONLY; The URL to get the next set of deleted keys.
NextLink *string `json:"nextLink,omitempty"`
}
@ -1367,9 +1307,9 @@ type DeletedSecretBundle struct {
autorest.Response `json:"-"`
// RecoveryID - The url of the recovery object, used to identify and recover the deleted secret.
RecoveryID *string `json:"recoveryId,omitempty"`
// ScheduledPurgeDate - The time when the secret is scheduled to be purged, in UTC
// ScheduledPurgeDate - READ-ONLY; The time when the secret is scheduled to be purged, in UTC
ScheduledPurgeDate *date.UnixTime `json:"scheduledPurgeDate,omitempty"`
// DeletedDate - The time when the secret was deleted, in UTC
// DeletedDate - READ-ONLY; The time when the secret was deleted, in UTC
DeletedDate *date.UnixTime `json:"deletedDate,omitempty"`
// Value - The secret value.
Value *string `json:"value,omitempty"`
@ -1381,9 +1321,9 @@ type DeletedSecretBundle struct {
Attributes *SecretAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
// Kid - If this is a secret backing a KV certificate, then this field specifies the corresponding key backing the KV certificate.
// Kid - READ-ONLY; If this is a secret backing a KV certificate, then this field specifies the corresponding key backing the KV certificate.
Kid *string `json:"kid,omitempty"`
// Managed - True if the secret's lifetime is managed by key vault. If this is a secret backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the secret's lifetime is managed by key vault. If this is a secret backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -1393,12 +1333,6 @@ func (dsb DeletedSecretBundle) MarshalJSON() ([]byte, error) {
if dsb.RecoveryID != nil {
objectMap["recoveryId"] = dsb.RecoveryID
}
if dsb.ScheduledPurgeDate != nil {
objectMap["scheduledPurgeDate"] = dsb.ScheduledPurgeDate
}
if dsb.DeletedDate != nil {
objectMap["deletedDate"] = dsb.DeletedDate
}
if dsb.Value != nil {
objectMap["value"] = dsb.Value
}
@ -1414,12 +1348,6 @@ func (dsb DeletedSecretBundle) MarshalJSON() ([]byte, error) {
if dsb.Tags != nil {
objectMap["tags"] = dsb.Tags
}
if dsb.Kid != nil {
objectMap["kid"] = dsb.Kid
}
if dsb.Managed != nil {
objectMap["managed"] = dsb.Managed
}
return json.Marshal(objectMap)
}
@ -1427,9 +1355,9 @@ func (dsb DeletedSecretBundle) MarshalJSON() ([]byte, error) {
type DeletedSecretItem struct {
// RecoveryID - The url of the recovery object, used to identify and recover the deleted secret.
RecoveryID *string `json:"recoveryId,omitempty"`
// ScheduledPurgeDate - The time when the secret is scheduled to be purged, in UTC
// ScheduledPurgeDate - READ-ONLY; The time when the secret is scheduled to be purged, in UTC
ScheduledPurgeDate *date.UnixTime `json:"scheduledPurgeDate,omitempty"`
// DeletedDate - The time when the secret was deleted, in UTC
// DeletedDate - READ-ONLY; The time when the secret was deleted, in UTC
DeletedDate *date.UnixTime `json:"deletedDate,omitempty"`
// ID - Secret identifier.
ID *string `json:"id,omitempty"`
@ -1439,7 +1367,7 @@ type DeletedSecretItem struct {
Tags map[string]*string `json:"tags"`
// ContentType - Type of the secret value such as a password.
ContentType *string `json:"contentType,omitempty"`
// Managed - True if the secret's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the secret's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -1449,12 +1377,6 @@ func (dsi DeletedSecretItem) MarshalJSON() ([]byte, error) {
if dsi.RecoveryID != nil {
objectMap["recoveryId"] = dsi.RecoveryID
}
if dsi.ScheduledPurgeDate != nil {
objectMap["scheduledPurgeDate"] = dsi.ScheduledPurgeDate
}
if dsi.DeletedDate != nil {
objectMap["deletedDate"] = dsi.DeletedDate
}
if dsi.ID != nil {
objectMap["id"] = dsi.ID
}
@ -1467,18 +1389,15 @@ func (dsi DeletedSecretItem) MarshalJSON() ([]byte, error) {
if dsi.ContentType != nil {
objectMap["contentType"] = dsi.ContentType
}
if dsi.Managed != nil {
objectMap["managed"] = dsi.Managed
}
return json.Marshal(objectMap)
}
// DeletedSecretListResult the deleted secret list result
type DeletedSecretListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of the deleted secrets in the vault along with a link to the next page of deleted secrets
// Value - READ-ONLY; A response message containing a list of the deleted secrets in the vault along with a link to the next page of deleted secrets
Value *[]DeletedSecretItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of deleted secrets.
// NextLink - READ-ONLY; The URL to get the next set of deleted secrets.
NextLink *string `json:"nextLink,omitempty"`
}
@ -1621,15 +1540,17 @@ func NewDeletedSecretListResultPage(getNextPage func(context.Context, DeletedSec
// Error the key vault server error.
type Error struct {
// Code - The error code.
// Code - READ-ONLY; The error code.
Code *string `json:"code,omitempty"`
// Message - The error message.
Message *string `json:"message,omitempty"`
InnerError *Error `json:"innererror,omitempty"`
// Message - READ-ONLY; The error message.
Message *string `json:"message,omitempty"`
// InnerError - READ-ONLY
InnerError *Error `json:"innererror,omitempty"`
}
// ErrorType the key vault error exception.
type ErrorType struct {
// Error - READ-ONLY
Error *Error `json:"error,omitempty"`
}
@ -1637,16 +1558,16 @@ type ErrorType struct {
type IssuerAttributes struct {
// Enabled - Determines whether the issuer is enabled.
Enabled *bool `json:"enabled,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
// IssuerBundle the issuer for Key Vault certificate.
type IssuerBundle struct {
autorest.Response `json:"-"`
// ID - Identifier for the issuer object.
// ID - READ-ONLY; Identifier for the issuer object.
ID *string `json:"id,omitempty"`
// Provider - The issuer provider.
Provider *string `json:"provider,omitempty"`
@ -1711,7 +1632,7 @@ type JSONWebKey struct {
// KeyAttributes the attributes of a key managed by the key vault service.
type KeyAttributes struct {
// RecoveryLevel - Reflects the deletion recovery level currently in effect for keys in the current vault. If it contains 'Purgeable' the key can be permanently deleted by a privileged user; otherwise, only the system can purge the key, at the end of the retention interval. Possible values include: 'Purgeable', 'RecoverablePurgeable', 'Recoverable', 'RecoverableProtectedSubscription'
// RecoveryLevel - READ-ONLY; Reflects the deletion recovery level currently in effect for keys in the current vault. If it contains 'Purgeable' the key can be permanently deleted by a privileged user; otherwise, only the system can purge the key, at the end of the retention interval. Possible values include: 'Purgeable', 'RecoverablePurgeable', 'Recoverable', 'RecoverableProtectedSubscription'
RecoveryLevel DeletionRecoveryLevel `json:"recoveryLevel,omitempty"`
// Enabled - Determines whether the object is enabled.
Enabled *bool `json:"enabled,omitempty"`
@ -1719,9 +1640,9 @@ type KeyAttributes struct {
NotBefore *date.UnixTime `json:"nbf,omitempty"`
// Expires - Expiry date in UTC.
Expires *date.UnixTime `json:"exp,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
@ -1734,7 +1655,7 @@ type KeyBundle struct {
Attributes *KeyAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
// Managed - True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -1750,9 +1671,6 @@ func (kb KeyBundle) MarshalJSON() ([]byte, error) {
if kb.Tags != nil {
objectMap["tags"] = kb.Tags
}
if kb.Managed != nil {
objectMap["managed"] = kb.Managed
}
return json.Marshal(objectMap)
}
@ -1832,7 +1750,7 @@ type KeyItem struct {
Attributes *KeyAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
// Managed - True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the key's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -1848,18 +1766,15 @@ func (ki KeyItem) MarshalJSON() ([]byte, error) {
if ki.Tags != nil {
objectMap["tags"] = ki.Tags
}
if ki.Managed != nil {
objectMap["managed"] = ki.Managed
}
return json.Marshal(objectMap)
}
// KeyListResult the key list result.
type KeyListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of keys in the key vault along with a link to the next page of keys.
// Value - READ-ONLY; A response message containing a list of keys in the key vault along with a link to the next page of keys.
Value *[]KeyItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of keys.
// NextLink - READ-ONLY; The URL to get the next set of keys.
NextLink *string `json:"nextLink,omitempty"`
}
@ -2003,9 +1918,9 @@ func NewKeyListResultPage(getNextPage func(context.Context, KeyListResult) (KeyL
// KeyOperationResult the key operation result.
type KeyOperationResult struct {
autorest.Response `json:"-"`
// Kid - Key identifier
// Kid - READ-ONLY; Key identifier
Kid *string `json:"kid,omitempty"`
// Result - a URL-encoded base64 string
// Result - READ-ONLY; a URL-encoded base64 string
Result *string `json:"value,omitempty"`
}
@ -2080,7 +1995,7 @@ type KeyVerifyParameters struct {
// KeyVerifyResult the key verify result.
type KeyVerifyResult struct {
autorest.Response `json:"-"`
// Value - True if the signature is verified, otherwise false.
// Value - READ-ONLY; True if the signature is verified, otherwise false.
Value *bool `json:"value,omitempty"`
}
@ -2103,7 +2018,7 @@ type OrganizationDetails struct {
// PendingCertificateSigningRequestResult the pending certificate signing request result.
type PendingCertificateSigningRequestResult struct {
// Value - The pending certificate signing request as Base64 encoded string.
// Value - READ-ONLY; The pending certificate signing request as Base64 encoded string.
Value *string `json:"value,omitempty"`
}
@ -2111,9 +2026,9 @@ type PendingCertificateSigningRequestResult struct {
type SasDefinitionAttributes struct {
// Enabled - the enabled state of the object.
Enabled *bool `json:"enabled,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
@ -2121,36 +2036,21 @@ type SasDefinitionAttributes struct {
// attributes.
type SasDefinitionBundle struct {
autorest.Response `json:"-"`
// ID - The SAS definition id.
// ID - READ-ONLY; The SAS definition id.
ID *string `json:"id,omitempty"`
// SecretID - Storage account SAS definition secret id.
// SecretID - READ-ONLY; Storage account SAS definition secret id.
SecretID *string `json:"sid,omitempty"`
// Parameters - The SAS definition metadata in the form of key-value pairs.
// Parameters - READ-ONLY; The SAS definition metadata in the form of key-value pairs.
Parameters map[string]*string `json:"parameters"`
// Attributes - The SAS definition attributes.
// Attributes - READ-ONLY; The SAS definition attributes.
Attributes *SasDefinitionAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs
// Tags - READ-ONLY; Application specific metadata in the form of key-value pairs
Tags map[string]*string `json:"tags"`
}
// MarshalJSON is the custom marshaler for SasDefinitionBundle.
func (sdb SasDefinitionBundle) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if sdb.ID != nil {
objectMap["id"] = sdb.ID
}
if sdb.SecretID != nil {
objectMap["sid"] = sdb.SecretID
}
if sdb.Parameters != nil {
objectMap["parameters"] = sdb.Parameters
}
if sdb.Attributes != nil {
objectMap["attributes"] = sdb.Attributes
}
if sdb.Tags != nil {
objectMap["tags"] = sdb.Tags
}
return json.Marshal(objectMap)
}
@ -2181,40 +2081,28 @@ func (sdcp SasDefinitionCreateParameters) MarshalJSON() ([]byte, error) {
// SasDefinitionItem the SAS definition item containing storage SAS definition metadata.
type SasDefinitionItem struct {
// ID - The storage SAS identifier.
// ID - READ-ONLY; The storage SAS identifier.
ID *string `json:"id,omitempty"`
// SecretID - The storage account SAS definition secret id.
// SecretID - READ-ONLY; The storage account SAS definition secret id.
SecretID *string `json:"sid,omitempty"`
// Attributes - The SAS definition management attributes.
// Attributes - READ-ONLY; The SAS definition management attributes.
Attributes *SasDefinitionAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
// Tags - READ-ONLY; Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
}
// MarshalJSON is the custom marshaler for SasDefinitionItem.
func (sdi SasDefinitionItem) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if sdi.ID != nil {
objectMap["id"] = sdi.ID
}
if sdi.SecretID != nil {
objectMap["sid"] = sdi.SecretID
}
if sdi.Attributes != nil {
objectMap["attributes"] = sdi.Attributes
}
if sdi.Tags != nil {
objectMap["tags"] = sdi.Tags
}
return json.Marshal(objectMap)
}
// SasDefinitionListResult the storage account SAS definition list result.
type SasDefinitionListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of SAS definitions along with a link to the next page of SAS definitions.
// Value - READ-ONLY; A response message containing a list of SAS definitions along with a link to the next page of SAS definitions.
Value *[]SasDefinitionItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of SAS definitions.
// NextLink - READ-ONLY; The URL to get the next set of SAS definitions.
NextLink *string `json:"nextLink,omitempty"`
}
@ -2382,7 +2270,7 @@ func (sdup SasDefinitionUpdateParameters) MarshalJSON() ([]byte, error) {
// SecretAttributes the secret management attributes.
type SecretAttributes struct {
// RecoveryLevel - Reflects the deletion recovery level currently in effect for secrets in the current vault. If it contains 'Purgeable', the secret can be permanently deleted by a privileged user; otherwise, only the system can purge the secret, at the end of the retention interval. Possible values include: 'Purgeable', 'RecoverablePurgeable', 'Recoverable', 'RecoverableProtectedSubscription'
// RecoveryLevel - READ-ONLY; Reflects the deletion recovery level currently in effect for secrets in the current vault. If it contains 'Purgeable', the secret can be permanently deleted by a privileged user; otherwise, only the system can purge the secret, at the end of the retention interval. Possible values include: 'Purgeable', 'RecoverablePurgeable', 'Recoverable', 'RecoverableProtectedSubscription'
RecoveryLevel DeletionRecoveryLevel `json:"recoveryLevel,omitempty"`
// Enabled - Determines whether the object is enabled.
Enabled *bool `json:"enabled,omitempty"`
@ -2390,9 +2278,9 @@ type SecretAttributes struct {
NotBefore *date.UnixTime `json:"nbf,omitempty"`
// Expires - Expiry date in UTC.
Expires *date.UnixTime `json:"exp,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
@ -2409,9 +2297,9 @@ type SecretBundle struct {
Attributes *SecretAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
// Kid - If this is a secret backing a KV certificate, then this field specifies the corresponding key backing the KV certificate.
// Kid - READ-ONLY; If this is a secret backing a KV certificate, then this field specifies the corresponding key backing the KV certificate.
Kid *string `json:"kid,omitempty"`
// Managed - True if the secret's lifetime is managed by key vault. If this is a secret backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the secret's lifetime is managed by key vault. If this is a secret backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -2433,12 +2321,6 @@ func (sb SecretBundle) MarshalJSON() ([]byte, error) {
if sb.Tags != nil {
objectMap["tags"] = sb.Tags
}
if sb.Kid != nil {
objectMap["kid"] = sb.Kid
}
if sb.Managed != nil {
objectMap["managed"] = sb.Managed
}
return json.Marshal(objectMap)
}
@ -2452,7 +2334,7 @@ type SecretItem struct {
Tags map[string]*string `json:"tags"`
// ContentType - Type of the secret value such as a password.
ContentType *string `json:"contentType,omitempty"`
// Managed - True if the secret's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
// Managed - READ-ONLY; True if the secret's lifetime is managed by key vault. If this is a key backing a certificate, then managed will be true.
Managed *bool `json:"managed,omitempty"`
}
@ -2471,18 +2353,15 @@ func (si SecretItem) MarshalJSON() ([]byte, error) {
if si.ContentType != nil {
objectMap["contentType"] = si.ContentType
}
if si.Managed != nil {
objectMap["managed"] = si.Managed
}
return json.Marshal(objectMap)
}
// SecretListResult the secret list result.
type SecretListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of secrets in the key vault along with a link to the next page of secrets.
// Value - READ-ONLY; A response message containing a list of secrets in the key vault along with a link to the next page of secrets.
Value *[]SecretItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of secrets.
// NextLink - READ-ONLY; The URL to get the next set of secrets.
NextLink *string `json:"nextLink,omitempty"`
}
@ -2694,9 +2573,9 @@ func (sup SecretUpdateParameters) MarshalJSON() ([]byte, error) {
type StorageAccountAttributes struct {
// Enabled - the enabled state of the object.
Enabled *bool `json:"enabled,omitempty"`
// Created - Creation time in UTC.
// Created - READ-ONLY; Creation time in UTC.
Created *date.UnixTime `json:"created,omitempty"`
// Updated - Last updated time in UTC.
// Updated - READ-ONLY; Last updated time in UTC.
Updated *date.UnixTime `json:"updated,omitempty"`
}
@ -2742,31 +2621,19 @@ func (sacp StorageAccountCreateParameters) MarshalJSON() ([]byte, error) {
// StorageAccountItem the storage account item containing storage account metadata.
type StorageAccountItem struct {
// ID - Storage identifier.
// ID - READ-ONLY; Storage identifier.
ID *string `json:"id,omitempty"`
// ResourceID - Storage account resource Id.
// ResourceID - READ-ONLY; Storage account resource Id.
ResourceID *string `json:"resourceId,omitempty"`
// Attributes - The storage account management attributes.
// Attributes - READ-ONLY; The storage account management attributes.
Attributes *StorageAccountAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs.
// Tags - READ-ONLY; Application specific metadata in the form of key-value pairs.
Tags map[string]*string `json:"tags"`
}
// MarshalJSON is the custom marshaler for StorageAccountItem.
func (sai StorageAccountItem) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if sai.ID != nil {
objectMap["id"] = sai.ID
}
if sai.ResourceID != nil {
objectMap["resourceId"] = sai.ResourceID
}
if sai.Attributes != nil {
objectMap["attributes"] = sai.Attributes
}
if sai.Tags != nil {
objectMap["tags"] = sai.Tags
}
return json.Marshal(objectMap)
}
@ -2815,55 +2682,34 @@ func (saup StorageAccountUpdateParameters) MarshalJSON() ([]byte, error) {
// attributes.
type StorageBundle struct {
autorest.Response `json:"-"`
// ID - The storage account id.
// ID - READ-ONLY; The storage account id.
ID *string `json:"id,omitempty"`
// ResourceID - The storage account resource id.
// ResourceID - READ-ONLY; The storage account resource id.
ResourceID *string `json:"resourceId,omitempty"`
// ActiveKeyName - The current active storage account key name.
// ActiveKeyName - READ-ONLY; The current active storage account key name.
ActiveKeyName *string `json:"activeKeyName,omitempty"`
// AutoRegenerateKey - whether keyvault should manage the storage account for the user.
// AutoRegenerateKey - READ-ONLY; whether keyvault should manage the storage account for the user.
AutoRegenerateKey *bool `json:"autoRegenerateKey,omitempty"`
// RegenerationPeriod - The key regeneration time duration specified in ISO-8601 format.
// RegenerationPeriod - READ-ONLY; The key regeneration time duration specified in ISO-8601 format.
RegenerationPeriod *string `json:"regenerationPeriod,omitempty"`
// Attributes - The storage account attributes.
// Attributes - READ-ONLY; The storage account attributes.
Attributes *StorageAccountAttributes `json:"attributes,omitempty"`
// Tags - Application specific metadata in the form of key-value pairs
// Tags - READ-ONLY; Application specific metadata in the form of key-value pairs
Tags map[string]*string `json:"tags"`
}
// MarshalJSON is the custom marshaler for StorageBundle.
func (sb StorageBundle) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if sb.ID != nil {
objectMap["id"] = sb.ID
}
if sb.ResourceID != nil {
objectMap["resourceId"] = sb.ResourceID
}
if sb.ActiveKeyName != nil {
objectMap["activeKeyName"] = sb.ActiveKeyName
}
if sb.AutoRegenerateKey != nil {
objectMap["autoRegenerateKey"] = sb.AutoRegenerateKey
}
if sb.RegenerationPeriod != nil {
objectMap["regenerationPeriod"] = sb.RegenerationPeriod
}
if sb.Attributes != nil {
objectMap["attributes"] = sb.Attributes
}
if sb.Tags != nil {
objectMap["tags"] = sb.Tags
}
return json.Marshal(objectMap)
}
// StorageListResult the storage accounts list result.
type StorageListResult struct {
autorest.Response `json:"-"`
// Value - A response message containing a list of storage accounts in the key vault along with a link to the next page of storage accounts.
// Value - READ-ONLY; A response message containing a list of storage accounts in the key vault along with a link to the next page of storage accounts.
Value *[]StorageAccountItem `json:"value,omitempty"`
// NextLink - The URL to get the next set of storage accounts.
// NextLink - READ-ONLY; The URL to get the next set of storage accounts.
NextLink *string `json:"nextLink,omitempty"`
}

View file

@ -616,11 +616,11 @@ type ResourceType struct {
// RoleAssignment role Assignments
type RoleAssignment struct {
autorest.Response `json:"-"`
// ID - The role assignment ID.
// ID - READ-ONLY; The role assignment ID.
ID *string `json:"id,omitempty"`
// Name - The role assignment name.
// Name - READ-ONLY; The role assignment name.
Name *string `json:"name,omitempty"`
// Type - The role assignment type.
// Type - READ-ONLY; The role assignment type.
Type *string `json:"type,omitempty"`
// RoleAssignmentPropertiesWithScope - Role assignment properties.
*RoleAssignmentPropertiesWithScope `json:"properties,omitempty"`
@ -629,15 +629,6 @@ type RoleAssignment struct {
// MarshalJSON is the custom marshaler for RoleAssignment.
func (ra RoleAssignment) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if ra.ID != nil {
objectMap["id"] = ra.ID
}
if ra.Name != nil {
objectMap["name"] = ra.Name
}
if ra.Type != nil {
objectMap["type"] = ra.Type
}
if ra.RoleAssignmentPropertiesWithScope != nil {
objectMap["properties"] = ra.RoleAssignmentPropertiesWithScope
}
@ -913,11 +904,11 @@ type RoleAssignmentPropertiesWithScope struct {
// RoleDefinition role definition.
type RoleDefinition struct {
autorest.Response `json:"-"`
// ID - The role definition ID.
// ID - READ-ONLY; The role definition ID.
ID *string `json:"id,omitempty"`
// Name - The role definition name.
// Name - READ-ONLY; The role definition name.
Name *string `json:"name,omitempty"`
// Type - The role definition type.
// Type - READ-ONLY; The role definition type.
Type *string `json:"type,omitempty"`
// RoleDefinitionProperties - Role definition properties.
*RoleDefinitionProperties `json:"properties,omitempty"`
@ -926,15 +917,6 @@ type RoleDefinition struct {
// MarshalJSON is the custom marshaler for RoleDefinition.
func (rd RoleDefinition) MarshalJSON() ([]byte, error) {
objectMap := make(map[string]interface{})
if rd.ID != nil {
objectMap["id"] = rd.ID
}
if rd.Name != nil {
objectMap["name"] = rd.Name
}
if rd.Type != nil {
objectMap["type"] = rd.Type
}
if rd.RoleDefinitionProperties != nil {
objectMap["properties"] = rd.RoleDefinitionProperties
}

View file

@ -89,6 +89,9 @@ func (client RoleDefinitionsClient) CreateOrUpdatePreparer(ctx context.Context,
"api-version": APIVersion,
}
roleDefinition.ID = nil
roleDefinition.Name = nil
roleDefinition.Type = nil
preparer := autorest.CreatePreparer(
autorest.AsContentType("application/json; charset=utf-8"),
autorest.AsPut(),

View file

@ -27,7 +27,7 @@ import (
"strings"
"time"
"github.com/satori/go.uuid"
uuid "github.com/satori/go.uuid"
)
// Annotating as secure for gas scanning
@ -257,6 +257,9 @@ func (e *Entity) MarshalJSON() ([]byte, error) {
case int64:
completeMap[typeKey] = OdataInt64
completeMap[k] = fmt.Sprintf("%v", v)
case float32, float64:
completeMap[typeKey] = OdataDouble
completeMap[k] = fmt.Sprintf("%v", v)
default:
completeMap[k] = v
}
@ -264,7 +267,8 @@ func (e *Entity) MarshalJSON() ([]byte, error) {
if !(completeMap[k] == OdataBinary ||
completeMap[k] == OdataDateTime ||
completeMap[k] == OdataGUID ||
completeMap[k] == OdataInt64) {
completeMap[k] == OdataInt64 ||
completeMap[k] == OdataDouble) {
return nil, fmt.Errorf("Odata.type annotation %v value is not valid", k)
}
valueKey := strings.TrimSuffix(k, OdataTypeSuffix)
@ -339,6 +343,12 @@ func (e *Entity) UnmarshalJSON(data []byte) error {
return fmt.Errorf(errorTemplate, err)
}
props[valueKey] = i
case OdataDouble:
f, err := strconv.ParseFloat(str, 64)
if err != nil {
return fmt.Errorf(errorTemplate, err)
}
props[valueKey] = f
default:
return fmt.Errorf(errorTemplate, fmt.Sprintf("%v is not supported", v))
}

View file

@ -26,6 +26,7 @@ const (
OdataBinary = "Edm.Binary"
OdataDateTime = "Edm.DateTime"
OdataDouble = "Edm.Double"
OdataGUID = "Edm.Guid"
OdataInt64 = "Edm.Int64"

View file

@ -25,8 +25,6 @@ import (
"net/textproto"
"sort"
"strings"
"github.com/marstr/guid"
)
// Operation type. Insert, Delete, Replace etc.
@ -132,8 +130,7 @@ func (t *TableBatch) MergeEntity(entity *Entity) {
// As per document https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/performing-entity-group-transactions
func (t *TableBatch) ExecuteBatch() error {
// Using `github.com/marstr/guid` is in response to issue #947 (https://github.com/Azure/azure-sdk-for-go/issues/947).
id, err := guid.NewGUIDs(guid.CreationStrategyVersion1)
id, err := newUUID()
if err != nil {
return err
}
@ -145,7 +142,7 @@ func (t *TableBatch) ExecuteBatch() error {
return err
}
id, err = guid.NewGUIDs(guid.CreationStrategyVersion1)
id, err = newUUID()
if err != nil {
return err
}

View file

@ -17,6 +17,7 @@ package storage
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/xml"
@ -29,6 +30,8 @@ import (
"strconv"
"strings"
"time"
uuid "github.com/satori/go.uuid"
)
var (
@ -242,3 +245,16 @@ func getMetadataFromHeaders(header http.Header) map[string]string {
return metadata
}
// newUUID returns a new uuid using RFC 4122 algorithm.
func newUUID() (uuid.UUID, error) {
u := [16]byte{}
// Set all bits to randomly (or pseudo-randomly) chosen values.
_, err := rand.Read(u[:])
if err != nil {
return uuid.UUID{}, err
}
u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) // u.setVariant(ReservedRFC4122)
u[6] = (u[6] & 0xF) | (uuid.V4 << 4) // u.setVersion(V4)
return uuid.FromBytes(u[:])
}

View file

@ -18,4 +18,4 @@ package version
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Number contains the semantic version of this SDK.
const Number = "v27.1.0"
const Number = "v29.0.0"

View file

@ -37,12 +37,12 @@ func signRoaRequest(request requests.AcsRequest, signer Signer, regionId string)
completeROASignParams(request, signer, regionId)
stringToSign := buildRoaStringToSign(request)
request.SetStringToSign(stringToSign)
signature := signer.Sign(stringToSign, "")
accessKeyId, err := signer.GetAccessKeyId()
if err != nil {
return nil
return err
}
signature := signer.Sign(stringToSign, "")
request.GetHeaders()["Authorization"] = "acs " + accessKeyId + ":" + signature
return

View file

@ -22,7 +22,7 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
)
var hookGetUUIDV4 = func(fn func() string) string {
var hookGetNonce = func(fn func() string) string {
return fn()
}
@ -52,7 +52,7 @@ func completeRpcSignParams(request requests.AcsRequest, signer Signer, regionId
queryParams["SignatureMethod"] = signer.GetName()
queryParams["SignatureType"] = signer.GetType()
queryParams["SignatureVersion"] = signer.GetVersion()
queryParams["SignatureNonce"] = hookGetUUIDV4(utils.GetUUIDV4)
queryParams["SignatureNonce"] = hookGetNonce(utils.GetUUID)
queryParams["AccessKeyId"], err = signer.GetAccessKeyId()
if err != nil {

View file

@ -403,6 +403,8 @@ func (client *Client) getTimeout(request requests.AcsRequest) (time.Duration, ti
readTimeout = reqReadTimeout
} else if client.readTimeout != 0*time.Millisecond {
readTimeout = client.readTimeout
} else if client.httpClient.Timeout != 0 && client.httpClient.Timeout != 10000000000 {
readTimeout = client.httpClient.Timeout
}
if reqConnectTimeout != 0*time.Millisecond {
@ -413,30 +415,24 @@ func (client *Client) getTimeout(request requests.AcsRequest) (time.Duration, ti
return readTimeout, connectTimeout
}
func Timeout(connectTimeout, readTimeout time.Duration) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
func Timeout(connectTimeout time.Duration) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
return func(ctx context.Context, network, address string) (net.Conn, error) {
conn, err := (&net.Dialer{
return (&net.Dialer{
Timeout: connectTimeout,
KeepAlive: 0 * time.Second,
DualStack: true,
}).DialContext(ctx, network, address)
if err == nil {
conn.SetDeadline(time.Now().Add(readTimeout))
}
return conn, err
}
}
func (client *Client) setTimeout(request requests.AcsRequest) {
readTimeout, connectTimeout := client.getTimeout(request)
client.httpClient.Timeout = readTimeout
if trans, ok := client.httpClient.Transport.(*http.Transport); ok && trans != nil {
trans.DialContext = Timeout(connectTimeout, readTimeout)
trans.DialContext = Timeout(connectTimeout)
client.httpClient.Transport = trans
} else {
client.httpClient.Transport = &http.Transport{
DialContext: Timeout(connectTimeout, readTimeout),
DialContext: Timeout(connectTimeout),
}
}
}
@ -501,21 +497,15 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
client.printLog(fieldMap, err)
initLogMsg(fieldMap)
}
putMsgToMap(fieldMap, httpRequest)
debug("> %s %s %s", httpRequest.Method, httpRequest.URL.RequestURI(), httpRequest.Proto)
debug("> Host: %s", httpRequest.Host)
fieldMap["{host}"] = httpRequest.Host
fieldMap["{method}"] = httpRequest.Method
fieldMap["{uri}"] = httpRequest.URL.RequestURI()
fieldMap["{pid}"] = strconv.Itoa(os.Getpid())
fieldMap["{version}"] = strings.Split(httpRequest.Proto, "/")[1]
hostname, _ := os.Hostname()
fieldMap["{hostname}"] = hostname
fieldMap["{req_headers}"] = TransToString(httpRequest.Header)
fieldMap["{target}"] = httpRequest.URL.Path + httpRequest.URL.RawQuery
for key, value := range httpRequest.Header {
debug("> %s: %v", key, strings.Join(value, ""))
}
debug(">")
debug(" Retry Times: %d.", retryTimes)
startTime := time.Now()
fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05")
httpResponse, err = hookDo(client.httpClient.Do)(httpRequest)
@ -531,15 +521,17 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
debug("<")
// receive error
if err != nil {
debug(" Error: %s.", err.Error())
if !client.config.AutoRetry {
return
} else if retryTimes >= client.config.MaxRetryTime {
// timeout but reached the max retry times, return
var timeoutErrorMsg string
if strings.Contains(err.Error(), "read tcp") {
timeoutErrorMsg = fmt.Sprintf(errors.TimeoutErrorMessage, strconv.Itoa(retryTimes+1), strconv.Itoa(retryTimes+1)) + " Read timeout. Please set a valid ReadTimeout."
times := strconv.Itoa(retryTimes + 1)
timeoutErrorMsg := fmt.Sprintf(errors.TimeoutErrorMessage, times, times)
if strings.Contains(err.Error(), "Client.Timeout") {
timeoutErrorMsg += " Read timeout. Please set a valid ReadTimeout."
} else {
timeoutErrorMsg = fmt.Sprintf(errors.TimeoutErrorMessage, strconv.Itoa(retryTimes+1), strconv.Itoa(retryTimes+1)) + " Connect timeout. Please set a valid ConnectTimeout."
timeoutErrorMsg += " Connect timeout. Please set a valid ConnectTimeout."
}
err = errors.NewClientError(errors.TimeoutErrorCode, timeoutErrorMsg, err)
return
@ -569,6 +561,18 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
return
}
func putMsgToMap(fieldMap map[string]string, request *http.Request) {
fieldMap["{host}"] = request.Host
fieldMap["{method}"] = request.Method
fieldMap["{uri}"] = request.URL.RequestURI()
fieldMap["{pid}"] = strconv.Itoa(os.Getpid())
fieldMap["{version}"] = strings.Split(request.Proto, "/")[1]
hostname, _ := os.Hostname()
fieldMap["{hostname}"] = hostname
fieldMap["{req_headers}"] = TransToString(request.Header)
fieldMap["{target}"] = request.URL.Path + request.URL.RawQuery
}
func buildHttpRequest(request requests.AcsRequest, singer auth.Signer, regionId string) (httpRequest *http.Request, err error) {
err = auth.Sign(request, singer, regionId)
if err != nil {

View file

@ -15,6 +15,7 @@
package requests
import (
"encoding/json"
"fmt"
"io"
"reflect"
@ -69,6 +70,7 @@ type AcsRequest interface {
GetStyle() string
GetProduct() string
GetVersion() string
SetVersion(version string)
GetActionName() string
GetAcceptFormat() string
GetLocationServiceCode() string
@ -165,6 +167,10 @@ func (request *baseRequest) GetContent() []byte {
return request.Content
}
func (request *baseRequest) SetVersion(version string) {
request.version = version
}
func (request *baseRequest) GetVersion() string {
return request.version
}
@ -313,6 +319,10 @@ func flatRepeatedList(dataValue reflect.Value, request AcsRequest, position, pre
// simple param
key := prefix + name
value := dataValue.Field(i).String()
if dataValue.Field(i).Kind().String() == "map" {
byt, _ := json.Marshal(dataValue.Field(i).Interface())
value = string(byt)
}
err = addParam(request, fieldPosition, key, value)
if err != nil {
return

View file

@ -11,9 +11,9 @@ import (
type CommonRequest struct {
*baseRequest
Version string
ApiName string
Product string
Version string
ApiName string
Product string
ServiceCode string
// roa params

View file

@ -136,7 +136,7 @@ func (request *RoaRequest) InitWithApiInfo(product, version, action, uriPattern,
request.locationEndpointType = endpointType
request.product = product
//request.version = version
//request.actionName = action
request.actionName = action
}
func (request *RoaRequest) initWithCommonRequest(commonRequest *CommonRequest) {
@ -145,7 +145,7 @@ func (request *RoaRequest) initWithCommonRequest(commonRequest *CommonRequest) {
request.product = commonRequest.Product
//request.version = commonRequest.Version
request.Headers["x-acs-version"] = commonRequest.Version
//request.actionName = commonRequest.ApiName
request.actionName = commonRequest.ApiName
request.pathPattern = commonRequest.PathPattern
request.locationServiceCode = commonRequest.ServiceCode
request.locationEndpointType = ""

View file

@ -16,22 +16,35 @@ package utils
import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"hash"
rand2 "math/rand"
"net/url"
"reflect"
"strconv"
"time"
"github.com/satori/go.uuid"
)
func GetUUIDV4() (uuidHex string) {
uuidV4 := uuid.NewV4()
uuidHex = hex.EncodeToString(uuidV4.Bytes())
type UUID [16]byte
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func GetUUID() (uuidHex string) {
uuid := NewUUID()
uuidHex = hex.EncodeToString(uuid[:])
return
}
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand2.Intn(len(letterBytes))]
}
return string(b)
}
func GetMD5Base64(bytes []byte) (base64Value string) {
md5Ctx := md5.New()
md5Ctx.Write(bytes)
@ -85,3 +98,44 @@ func InitStructWithDefaultTag(bean interface{}) {
}
}
}
func NewUUID() UUID {
ns := UUID{}
safeRandom(ns[:])
u := newFromHash(md5.New(), ns, RandStringBytes(16))
u[6] = (u[6] & 0x0f) | (byte(2) << 4)
u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
return u
}
func newFromHash(h hash.Hash, ns UUID, name string) UUID {
u := UUID{}
h.Write(ns[:])
h.Write([]byte(name))
copy(u[:], h.Sum(nil))
return u
}
func safeRandom(dest []byte) {
if _, err := rand.Read(dest); err != nil {
panic(err)
}
}
func (u UUID) String() string {
buf := make([]byte, 36)
hex.Encode(buf[0:8], u[0:4])
buf[8] = '-'
hex.Encode(buf[9:13], u[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], u[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], u[8:10])
buf[23] = '-'
hex.Encode(buf[24:], u[10:])
return string(buf)
}

View file

@ -91,7 +91,7 @@ func CreateAddUserToGroupRequest() (request *AddUserToGroupRequest) {
request = &AddUserToGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "AddUserToGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "AddUserToGroup", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateAttachPolicyToGroupRequest() (request *AttachPolicyToGroupRequest) {
request = &AttachPolicyToGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "AttachPolicyToGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "AttachPolicyToGroup", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateAttachPolicyToRoleRequest() (request *AttachPolicyToRoleRequest) {
request = &AttachPolicyToRoleRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "AttachPolicyToRole", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "AttachPolicyToRole", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateAttachPolicyToUserRequest() (request *AttachPolicyToUserRequest) {
request = &AttachPolicyToUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "AttachPolicyToUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "AttachPolicyToUser", "", "")
return
}

View file

@ -93,7 +93,7 @@ func CreateBindMFADeviceRequest() (request *BindMFADeviceRequest) {
request = &BindMFADeviceRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "BindMFADevice", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "BindMFADevice", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateChangePasswordRequest() (request *ChangePasswordRequest) {
request = &ChangePasswordRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ChangePassword", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ChangePassword", "", "")
return
}

View file

@ -89,7 +89,7 @@ func CreateClearAccountAliasRequest() (request *ClearAccountAliasRequest) {
request = &ClearAccountAliasRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ClearAccountAlias", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ClearAccountAlias", "", "")
return
}

View file

@ -18,6 +18,7 @@ package ram
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider"
)
// Client is the sdk client struct, each func corresponds to an OpenAPI
@ -32,6 +33,20 @@ func NewClient() (client *Client, err error) {
return
}
// NewClientWithProvider creates a sdk client with providers
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithProvider(regionId string, providers ...provider.Provider) (client *Client, err error) {
client = &Client{}
var pc provider.Provider
if len(providers) == 0 {
pc = provider.DefaultChain
} else {
pc = provider.NewProviderChain(providers)
}
err = client.InitWithProviderChain(regionId, pc)
return
}
// NewClientWithOptions creates a sdk client with regionId/sdkConfig/credential
// this is the common api to create a sdk client
func NewClientWithOptions(regionId string, config *sdk.Config, credential auth.Credential) (client *Client, err error) {
@ -41,7 +56,7 @@ func NewClientWithOptions(regionId string, config *sdk.Config, credential auth.C
}
// NewClientWithAccessKey is a shortcut to create sdk client with accesskey
// usage: https://help.aliyun.com/document_detail/66217.html
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret string) (client *Client, err error) {
client = &Client{}
err = client.InitWithAccessKey(regionId, accessKeyId, accessKeySecret)
@ -49,7 +64,7 @@ func NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret string) (clie
}
// NewClientWithStsToken is a shortcut to create sdk client with sts token
// usage: https://help.aliyun.com/document_detail/66222.html
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken string) (client *Client, err error) {
client = &Client{}
err = client.InitWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken)
@ -57,15 +72,23 @@ func NewClientWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToke
}
// NewClientWithRamRoleArn is a shortcut to create sdk client with ram roleArn
// usage: https://help.aliyun.com/document_detail/66222.html
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithRamRoleArn(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (client *Client, err error) {
client = &Client{}
err = client.InitWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName)
return
}
// NewClientWithRamRoleArn is a shortcut to create sdk client with ram roleArn and policy
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithRamRoleArnAndPolicy(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName, policy string) (client *Client, err error) {
client = &Client{}
err = client.InitWithRamRoleArnAndPolicy(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName, policy)
return
}
// NewClientWithEcsRamRole is a shortcut to create sdk client with ecs ram role
// usage: https://help.aliyun.com/document_detail/66223.html
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithEcsRamRole(regionId string, roleName string) (client *Client, err error) {
client = &Client{}
err = client.InitWithEcsRamRole(regionId, roleName)
@ -73,7 +96,7 @@ func NewClientWithEcsRamRole(regionId string, roleName string) (client *Client,
}
// NewClientWithRsaKeyPair is a shortcut to create sdk client with rsa key pair
// attention: rsa key pair auth is only Japan regions available
// usage: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/docs/2-Client-EN.md
func NewClientWithRsaKeyPair(regionId string, publicKeyId, privateKey string, sessionExpiration int) (client *Client, err error) {
client = &Client{}
err = client.InitWithRsaKeyPair(regionId, publicKeyId, privateKey, sessionExpiration)

View file

@ -91,7 +91,7 @@ func CreateCreateAccessKeyRequest() (request *CreateAccessKeyRequest) {
request = &CreateAccessKeyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreateAccessKey", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreateAccessKey", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateCreateGroupRequest() (request *CreateGroupRequest) {
request = &CreateGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreateGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreateGroup", "", "")
return
}

View file

@ -94,7 +94,7 @@ func CreateCreateLoginProfileRequest() (request *CreateLoginProfileRequest) {
request = &CreateLoginProfileRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreateLoginProfile", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreateLoginProfile", "", "")
return
}

View file

@ -93,7 +93,7 @@ func CreateCreatePolicyRequest() (request *CreatePolicyRequest) {
request = &CreatePolicyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreatePolicy", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreatePolicy", "", "")
return
}

View file

@ -79,6 +79,7 @@ type CreatePolicyVersionRequest struct {
SetAsDefault requests.Boolean `position:"Query" name:"SetAsDefault"`
PolicyName string `position:"Query" name:"PolicyName"`
PolicyDocument string `position:"Query" name:"PolicyDocument"`
RotateStrategy string `position:"Query" name:"RotateStrategy"`
}
// CreatePolicyVersionResponse is the response struct for api CreatePolicyVersion
@ -93,7 +94,7 @@ func CreateCreatePolicyVersionRequest() (request *CreatePolicyVersionRequest) {
request = &CreatePolicyVersionRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreatePolicyVersion", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreatePolicyVersion", "", "")
return
}

View file

@ -93,7 +93,7 @@ func CreateCreateRoleRequest() (request *CreateRoleRequest) {
request = &CreateRoleRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreateRole", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreateRole", "", "")
return
}

View file

@ -95,7 +95,7 @@ func CreateCreateUserRequest() (request *CreateUserRequest) {
request = &CreateUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreateUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreateUser", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateCreateVirtualMFADeviceRequest() (request *CreateVirtualMFADeviceReque
request = &CreateVirtualMFADeviceRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "CreateVirtualMFADevice", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "CreateVirtualMFADevice", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateDeleteAccessKeyRequest() (request *DeleteAccessKeyRequest) {
request = &DeleteAccessKeyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteAccessKey", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteAccessKey", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateDeleteGroupRequest() (request *DeleteGroupRequest) {
request = &DeleteGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteGroup", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateDeleteLoginProfileRequest() (request *DeleteLoginProfileRequest) {
request = &DeleteLoginProfileRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteLoginProfile", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteLoginProfile", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateDeletePolicyRequest() (request *DeletePolicyRequest) {
request = &DeletePolicyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeletePolicy", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeletePolicy", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateDeletePolicyVersionRequest() (request *DeletePolicyVersionRequest) {
request = &DeletePolicyVersionRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeletePolicyVersion", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeletePolicyVersion", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateDeletePublicKeyRequest() (request *DeletePublicKeyRequest) {
request = &DeletePublicKeyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeletePublicKey", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeletePublicKey", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateDeleteRoleRequest() (request *DeleteRoleRequest) {
request = &DeleteRoleRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteRole", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteRole", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateDeleteUserRequest() (request *DeleteUserRequest) {
request = &DeleteUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteUser", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateDeleteVirtualMFADeviceRequest() (request *DeleteVirtualMFADeviceReque
request = &DeleteVirtualMFADeviceRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteVirtualMFADevice", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DeleteVirtualMFADevice", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateDetachPolicyFromGroupRequest() (request *DetachPolicyFromGroupRequest
request = &DetachPolicyFromGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DetachPolicyFromGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DetachPolicyFromGroup", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateDetachPolicyFromRoleRequest() (request *DetachPolicyFromRoleRequest)
request = &DetachPolicyFromRoleRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DetachPolicyFromRole", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DetachPolicyFromRole", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateDetachPolicyFromUserRequest() (request *DetachPolicyFromUserRequest)
request = &DetachPolicyFromUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "DetachPolicyFromUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "DetachPolicyFromUser", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateGetAccessKeyLastUsedRequest() (request *GetAccessKeyLastUsedRequest)
request = &GetAccessKeyLastUsedRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetAccessKeyLastUsed", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetAccessKeyLastUsed", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateGetAccountAliasRequest() (request *GetAccountAliasRequest) {
request = &GetAccountAliasRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetAccountAlias", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetAccountAlias", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateGetGroupRequest() (request *GetGroupRequest) {
request = &GetGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetGroup", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateGetLoginProfileRequest() (request *GetLoginProfileRequest) {
request = &GetLoginProfileRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetLoginProfile", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetLoginProfile", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateGetPasswordPolicyRequest() (request *GetPasswordPolicyRequest) {
request = &GetPasswordPolicyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetPasswordPolicy", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetPasswordPolicy", "", "")
return
}

View file

@ -83,8 +83,9 @@ type GetPolicyRequest struct {
// GetPolicyResponse is the response struct for api GetPolicy
type GetPolicyResponse struct {
*responses.BaseResponse
RequestId string `json:"RequestId" xml:"RequestId"`
Policy Policy `json:"Policy" xml:"Policy"`
RequestId string `json:"RequestId" xml:"RequestId"`
Policy Policy `json:"Policy" xml:"Policy"`
DefaultPolicyVersion DefaultPolicyVersion `json:"DefaultPolicyVersion" xml:"DefaultPolicyVersion"`
}
// CreateGetPolicyRequest creates a request to invoke GetPolicy API
@ -92,7 +93,7 @@ func CreateGetPolicyRequest() (request *GetPolicyRequest) {
request = &GetPolicyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetPolicy", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetPolicy", "", "")
return
}

View file

@ -93,7 +93,7 @@ func CreateGetPolicyVersionRequest() (request *GetPolicyVersionRequest) {
request = &GetPolicyVersionRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetPolicyVersion", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetPolicyVersion", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateGetPublicKeyRequest() (request *GetPublicKeyRequest) {
request = &GetPublicKeyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetPublicKey", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetPublicKey", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateGetRoleRequest() (request *GetRoleRequest) {
request = &GetRoleRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetRole", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetRole", "", "")
return
}

View file

@ -90,7 +90,7 @@ func CreateGetSecurityPreferenceRequest() (request *GetSecurityPreferenceRequest
request = &GetSecurityPreferenceRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetSecurityPreference", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetSecurityPreference", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateGetUserRequest() (request *GetUserRequest) {
request = &GetUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetUser", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateGetUserMFAInfoRequest() (request *GetUserMFAInfoRequest) {
request = &GetUserMFAInfoRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "GetUserMFAInfo", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "GetUserMFAInfo", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateListAccessKeysRequest() (request *ListAccessKeysRequest) {
request = &ListAccessKeysRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListAccessKeys", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListAccessKeys", "", "")
return
}

View file

@ -94,7 +94,7 @@ func CreateListEntitiesForPolicyRequest() (request *ListEntitiesForPolicyRequest
request = &ListEntitiesForPolicyRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListEntitiesForPolicy", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListEntitiesForPolicy", "", "")
return
}

View file

@ -94,7 +94,7 @@ func CreateListGroupsRequest() (request *ListGroupsRequest) {
request = &ListGroupsRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListGroups", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListGroups", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateListGroupsForUserRequest() (request *ListGroupsForUserRequest) {
request = &ListGroupsForUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListGroupsForUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListGroupsForUser", "", "")
return
}

View file

@ -95,7 +95,7 @@ func CreateListPoliciesRequest() (request *ListPoliciesRequest) {
request = &ListPoliciesRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListPolicies", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListPolicies", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateListPoliciesForGroupRequest() (request *ListPoliciesForGroupRequest)
request = &ListPoliciesForGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListPoliciesForGroup", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListPoliciesForGroup", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateListPoliciesForRoleRequest() (request *ListPoliciesForRoleRequest) {
request = &ListPoliciesForRoleRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListPoliciesForRole", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListPoliciesForRole", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateListPoliciesForUserRequest() (request *ListPoliciesForUserRequest) {
request = &ListPoliciesForUserRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListPoliciesForUser", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListPoliciesForUser", "", "")
return
}

View file

@ -92,7 +92,7 @@ func CreateListPolicyVersionsRequest() (request *ListPolicyVersionsRequest) {
request = &ListPolicyVersionsRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListPolicyVersions", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListPolicyVersions", "", "")
return
}

View file

@ -91,7 +91,7 @@ func CreateListPublicKeysRequest() (request *ListPublicKeysRequest) {
request = &ListPublicKeysRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Ram", "2015-05-01", "ListPublicKeys", "ram", "openAPI")
request.InitWithApiInfo("Ram", "2015-05-01", "ListPublicKeys", "", "")
return
}

Some files were not shown because too many files have changed in this diff Show more