Update 'images' to v0.113.0

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
Tomáš Hozza 2025-02-03 14:26:54 +01:00 committed by Achilleas Koutsou
parent b8c2e4c45c
commit 8514c95837
646 changed files with 36206 additions and 22388 deletions

View file

@ -1,2 +1,2 @@
6.4.0
7.0.1
# Keep this pinned version in parity with cel-go

View file

@ -1 +1,2 @@
bazel-*
MODULE.bazel.lock

View file

@ -16,7 +16,7 @@ go_library(
importpath = "cel.dev/expr",
visibility = ["//visibility:public"],
deps = [
"//proto/cel/expr:google_rpc_status_go_proto",
"@org_golang_google_genproto_googleapis_rpc//status:go_default_library",
"@org_golang_google_protobuf//reflect/protoreflect",
"@org_golang_google_protobuf//runtime/protoimpl",
"@org_golang_google_protobuf//types/known/anypb",

70
vendor/cel.dev/expr/MODULE.bazel vendored Normal file
View file

@ -0,0 +1,70 @@
module(
name = "cel-spec",
)
bazel_dep(
name = "bazel_skylib",
version = "1.7.1",
)
bazel_dep(
name = "gazelle",
version = "0.36.0",
repo_name = "bazel_gazelle",
)
bazel_dep(
name = "googleapis",
version = "0.0.0-20240819-fe8ba054a",
repo_name = "com_google_googleapis",
)
bazel_dep(
name = "protobuf",
version = "26.0",
repo_name = "com_google_protobuf",
)
bazel_dep(
name = "rules_cc",
version = "0.0.9",
)
bazel_dep(
name = "rules_go",
version = "0.49.0",
repo_name = "io_bazel_rules_go",
)
bazel_dep(
name = "rules_java",
version = "7.6.5",
)
bazel_dep(
name = "rules_proto",
version = "6.0.0",
)
bazel_dep(
name = "rules_python",
version = "0.35.0",
)
### PYTHON ###
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
ignore_root_user_error = True,
python_version = "3.11",
)
switched_rules = use_extension("@com_google_googleapis//:extensions.bzl", "switched_rules")
switched_rules.use_languages(
cc = True,
go = True,
java = True,
)
use_repo(switched_rules, "com_google_googleapis_imports")
go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.21.1")
go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(
go_deps,
"org_golang_google_genproto_googleapis_rpc",
"org_golang_google_protobuf",
)

View file

@ -33,8 +33,7 @@ The required components of a system that supports CEL are:
* The textual representation of an expression as written by a developer. It is
of similar syntax to expressions in C/C++/Java/JavaScript
* A binary representation of an expression. It is an abstract syntax tree
(AST).
* A representation of the program's abstract syntax tree (AST).
* A compiler library that converts the textual representation to the binary
representation. This can be done ahead of time (in the control plane) or
just before evaluation (in the data plane).
@ -43,6 +42,15 @@ The required components of a system that supports CEL are:
* An evaluator library that takes the binary format in the context and
produces a result, usually a Boolean.
For use cases which require persistence or cross-process communcation, it is
highly recommended to serialize the type-checked expression as a protocol
buffer. The CEL team will maintains canonical protocol buffers for ASTs and
will keep these versions identical and wire-compatible in perpetuity:
* [CEL canonical](https://github.com/google/cel-spec/tree/master/proto/cel/expr)
* [CEL v1alpha1](https://github.com/googleapis/googleapis/tree/master/google/api/expr/v1alpha1)
Example of boolean conditions and object construction:
``` c

View file

@ -27,13 +27,13 @@ http_archive(
],
)
# googleapis as of 05/26/2023
# googleapis as of 09/16/2024
http_archive(
name = "com_google_googleapis",
strip_prefix = "googleapis-07c27163ac591955d736f3057b1619ece66f5b99",
sha256 = "bd8e735d881fb829751ecb1a77038dda4a8d274c45490cb9fcf004583ee10571",
strip_prefix = "googleapis-4082d5e51e8481f6ccc384cacd896f4e78f19dee",
sha256 = "57319889d47578b3c89bf1b3f34888d796a8913d63b32d750a4cd12ed303c4e8",
urls = [
"https://github.com/googleapis/googleapis/archive/07c27163ac591955d736f3057b1619ece66f5b99.tar.gz",
"https://github.com/googleapis/googleapis/archive/4082d5e51e8481f6ccc384cacd896f4e78f19dee.tar.gz",
],
)
@ -95,22 +95,22 @@ switched_rules_by_language(
# Do *not* call *_dependencies(), etc, yet. See comment at the end.
# Generated Google APIs protos for Golang
# Generated Google APIs protos for Golang 05/25/2023
# Generated Google APIs protos for Golang 08/26/2024
go_repository(
name = "org_golang_google_genproto_googleapis_api",
build_file_proto_mode = "disable_global",
importpath = "google.golang.org/genproto/googleapis/api",
sum = "h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ=",
version = "v0.0.0-20230525234035-dd9d682886f9",
sum = "h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=",
version = "v0.0.0-20240826202546-f6391c0de4c7",
)
# Generated Google APIs protos for Golang 05/25/2023
# Generated Google APIs protos for Golang 08/26/2024
go_repository(
name = "org_golang_google_genproto_googleapis_rpc",
build_file_proto_mode = "disable_global",
importpath = "google.golang.org/genproto/googleapis/rpc",
sum = "h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=",
version = "v0.0.0-20230525234030-28d5490b6b19",
sum = "h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=",
version = "v0.0.0-20240826202546-f6391c0de4c7",
)
# gRPC deps

0
vendor/cel.dev/expr/WORKSPACE.bzlmod vendored Normal file
View file

View file

@ -1,8 +1,8 @@
steps:
- name: 'gcr.io/cloud-builders/bazel:6.4.0'
- name: 'gcr.io/cloud-builders/bazel:7.0.1'
entrypoint: bazel
args: ['test', '--test_output=errors', '...']
id: bazel-test
args: ['build', '...']
id: bazel-build
waitFor: ['-']
timeout: 15m
options:

View file

@ -1,9 +1,9 @@
#!/bin/sh
bazel build //proto/test/...
files=($(bazel aquery 'kind(proto, //proto/...)' | grep Outputs | grep "[.]pb[.]go" | sed 's/Outputs: \[//' | sed 's/\]//' | tr "," "\n"))
bazel build //proto/cel/expr/conformance/...
files=($(bazel aquery 'kind(proto, //proto/cel/expr/conformance/...)' | grep Outputs | grep "[.]pb[.]go" | sed 's/Outputs: \[//' | sed 's/\]//' | tr "," "\n"))
for src in ${files[@]};
do
dst=$(echo $src | sed 's/\(.*\%\/github.com\/google\/cel-spec\/\(.*\)\)/\2/')
dst=$(echo $src | sed 's/\(.*\/cel.dev\/expr\/\(.*\)\)/\2/')
echo "copying $dst"
$(cp $src $dst)
done

View file

@ -1,5 +1,19 @@
# Changelog
## [0.14.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.13.0...auth/v0.14.0) (2025-01-08)
### Features
* **auth:** Add universe domain support to idtoken ([#11059](https://github.com/googleapis/google-cloud-go/issues/11059)) ([72add7e](https://github.com/googleapis/google-cloud-go/commit/72add7e9f8f455af695e8ef79212a4bd3122fb3a))
### Bug Fixes
* **auth/oauth2adapt:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
* **auth:** Fix copy of delegates in impersonate.NewIDTokenCredentials ([#11386](https://github.com/googleapis/google-cloud-go/issues/11386)) ([ff7ef8e](https://github.com/googleapis/google-cloud-go/commit/ff7ef8e7ade7171bce3e4f30ff10a2e9f6c27ca0)), refs [#11379](https://github.com/googleapis/google-cloud-go/issues/11379)
* **auth:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
## [0.13.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.12.1...auth/v0.13.0) (2024-12-13)

View file

@ -0,0 +1,105 @@
// Copyright 2025 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 impersonate
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"strings"
"time"
"cloud.google.com/go/auth"
"cloud.google.com/go/auth/internal"
"github.com/googleapis/gax-go/v2/internallog"
)
var (
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
iamCredentialsUniverseDomainEndpoint = "https://iamcredentials.UNIVERSE_DOMAIN"
)
// IDTokenIAMOptions provides configuration for [IDTokenIAMOptions.Token].
type IDTokenIAMOptions struct {
// Client is required.
Client *http.Client
// Logger is required.
Logger *slog.Logger
UniverseDomain auth.CredentialsPropertyProvider
ServiceAccountEmail string
GenerateIDTokenRequest
}
// GenerateIDTokenRequest holds the request to the IAM generateIdToken RPC.
type GenerateIDTokenRequest struct {
Audience string `json:"audience"`
IncludeEmail bool `json:"includeEmail"`
// Delegates are the ordered, fully-qualified resource name for service
// accounts in a delegation chain. Each service account must be granted
// roles/iam.serviceAccountTokenCreator on the next service account in the
// chain. The delegates must have the following format:
// projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}. The - wildcard
// character is required; replacing it with a project ID is invalid.
// Optional.
Delegates []string `json:"delegates,omitempty"`
}
// GenerateIDTokenResponse holds the response from the IAM generateIdToken RPC.
type GenerateIDTokenResponse struct {
Token string `json:"token"`
}
// Token call IAM generateIdToken with the configuration provided in [IDTokenIAMOptions].
func (o IDTokenIAMOptions) Token(ctx context.Context) (*auth.Token, error) {
universeDomain, err := o.UniverseDomain.GetProperty(ctx)
if err != nil {
return nil, err
}
endpoint := strings.Replace(iamCredentialsUniverseDomainEndpoint, universeDomainPlaceholder, universeDomain, 1)
url := fmt.Sprintf("%s/v1/%s:generateIdToken", endpoint, internal.FormatIAMServiceAccountResource(o.ServiceAccountEmail))
bodyBytes, err := json.Marshal(o.GenerateIDTokenRequest)
if err != nil {
return nil, fmt.Errorf("impersonate: unable to marshal request: %w", err)
}
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(bodyBytes))
if err != nil {
return nil, fmt.Errorf("impersonate: unable to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
o.Logger.DebugContext(ctx, "impersonated idtoken request", "request", internallog.HTTPRequest(req, bodyBytes))
resp, body, err := internal.DoRequest(o.Client, req)
if err != nil {
return nil, fmt.Errorf("impersonate: unable to generate ID token: %w", err)
}
o.Logger.DebugContext(ctx, "impersonated idtoken response", "response", internallog.HTTPResponse(resp, body))
if c := resp.StatusCode; c < 200 || c > 299 {
return nil, fmt.Errorf("impersonate: status code %d: %s", c, body)
}
var tokenResp GenerateIDTokenResponse
if err := json.Unmarshal(body, &tokenResp); err != nil {
return nil, fmt.Errorf("impersonate: unable to parse response: %w", err)
}
return &auth.Token{
Value: tokenResp.Token,
// Generated ID tokens are good for one hour.
Expiry: time.Now().Add(1 * time.Hour),
}, nil
}

View file

@ -217,3 +217,9 @@ func getMetadataUniverseDomain(ctx context.Context, client *metadata.Client) (st
}
return "", err
}
// FormatIAMServiceAccountResource sets a service account name in an IAM resource
// name.
func FormatIAMServiceAccountResource(name string) string {
return fmt.Sprintf("projects/-/serviceAccounts/%s", name)
}

View file

@ -1,5 +1,12 @@
# Changelog
## [0.2.7](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.6...auth/oauth2adapt/v0.2.7) (2025-01-09)
### Bug Fixes
* **auth/oauth2adapt:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
## [0.2.6](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.5...auth/oauth2adapt/v0.2.6) (2024-11-21)

View file

@ -1,6 +1,109 @@
# Changes
## [1.50.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.49.0...storage/v1.50.0) (2025-01-09)
### Features
* **storage/internal:** Add new appendable Object to BidiWrite API ([2e4feb9](https://github.com/googleapis/google-cloud-go/commit/2e4feb938ce9ab023c8aa6bd1dbdf36fe589213a))
* **storage/internal:** Add new preview BidiReadObject API ([2e4feb9](https://github.com/googleapis/google-cloud-go/commit/2e4feb938ce9ab023c8aa6bd1dbdf36fe589213a))
* **storage:** Add support for gRPC bi-directional multi-range reads. This API is in private preview and not generally and is not yet available for general use. ([#11377](https://github.com/googleapis/google-cloud-go/issues/11377)) ([b4d86a5](https://github.com/googleapis/google-cloud-go/commit/b4d86a52bd319a602115cdb710a743c71494a88b))
* **storage:** Add support for ReadHandle, a gRPC feature that allows for accelerated resumption of streams when one is interrupted. ReadHandle requires the bi-directional read API, which is in private preview and is not yet available for general use. ([#11377](https://github.com/googleapis/google-cloud-go/issues/11377)) ([b4d86a5](https://github.com/googleapis/google-cloud-go/commit/b4d86a52bd319a602115cdb710a743c71494a88b))
* **storage:** Support appendable semantics for writes in gRPC. This API is in preview. ([#11377](https://github.com/googleapis/google-cloud-go/issues/11377)) ([b4d86a5](https://github.com/googleapis/google-cloud-go/commit/b4d86a52bd319a602115cdb710a743c71494a88b))
* **storage:** Refactor gRPC writer flow ([#11377](https://github.com/googleapis/google-cloud-go/issues/11377)) ([b4d86a5](https://github.com/googleapis/google-cloud-go/commit/b4d86a52bd319a602115cdb710a743c71494a88b))
### Bug Fixes
* **storage:** Add mutex around uses of mrd variables ([#11405](https://github.com/googleapis/google-cloud-go/issues/11405)) ([54bfc32](https://github.com/googleapis/google-cloud-go/commit/54bfc32db7a0ff40a493de4d466f21ad624de04e))
* **storage:** Return the appropriate error for method not supported ([#11416](https://github.com/googleapis/google-cloud-go/issues/11416)) ([56d704e](https://github.com/googleapis/google-cloud-go/commit/56d704e3037840aeb87b22cc83f2b6088c79bcee))
### Documentation
* **storage/internal:** Add IAM information to RPC comments for reference documentation ([2e4feb9](https://github.com/googleapis/google-cloud-go/commit/2e4feb938ce9ab023c8aa6bd1dbdf36fe589213a))
* **storage:** Add preview comment to NewMultiRangeDownloader ([#11420](https://github.com/googleapis/google-cloud-go/issues/11420)) ([4ec1d66](https://github.com/googleapis/google-cloud-go/commit/4ec1d66ee180e800606568e8693a282645ec7369))
## [1.49.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.48.0...storage/v1.49.0) (2024-12-21)
### Features
* **storage/internal:** Add finalize_time field in Object metadata ([46fc993](https://github.com/googleapis/google-cloud-go/commit/46fc993a3195203a230e2831bee456baaa9f7b1c))
* **storage/internal:** Add MoveObject RPC ([46fc993](https://github.com/googleapis/google-cloud-go/commit/46fc993a3195203a230e2831bee456baaa9f7b1c))
* **storage:** Add ObjectHandle.Move method ([#11302](https://github.com/googleapis/google-cloud-go/issues/11302)) ([a3cb8c4](https://github.com/googleapis/google-cloud-go/commit/a3cb8c4fc48883b54d4e830ae5f5ef4f1a3b8ca3))
* **storage:** Return file metadata on read ([#11212](https://github.com/googleapis/google-cloud-go/issues/11212)) ([d49263b](https://github.com/googleapis/google-cloud-go/commit/d49263b2ab614cad801e26b4a169eafe08d4a2a0))
### Bug Fixes
* **storage/dataflux:** Address deadlock when reading from ranges ([#11303](https://github.com/googleapis/google-cloud-go/issues/11303)) ([32cbf56](https://github.com/googleapis/google-cloud-go/commit/32cbf561590541eb0387787bf729be6ddf68e4ee))
* **storage:** Disable allow non-default credentials flag ([#11337](https://github.com/googleapis/google-cloud-go/issues/11337)) ([145ddf4](https://github.com/googleapis/google-cloud-go/commit/145ddf4f6123d9561856d2b6adeefdfae462b3f7))
* **storage:** Monitored resource detection ([#11197](https://github.com/googleapis/google-cloud-go/issues/11197)) ([911bcd8](https://github.com/googleapis/google-cloud-go/commit/911bcd8b1816256482bd52e85da7eaf00c315293))
* **storage:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
## [1.48.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.47.0...storage/v1.48.0) (2024-12-05)
### Features
* **storage/dataflux:** Run worksteal listing parallel to sequential listing ([#10966](https://github.com/googleapis/google-cloud-go/issues/10966)) ([3005f5a](https://github.com/googleapis/google-cloud-go/commit/3005f5a86c18254e569b8b1782bf014aa62f33cc))
* **storage:** Add Writer.ChunkTransferTimeout ([#11111](https://github.com/googleapis/google-cloud-go/issues/11111)) ([fd1db20](https://github.com/googleapis/google-cloud-go/commit/fd1db203d0de898891b9920aacb141ea39228609))
* **storage:** Allow non default service account ([#11137](https://github.com/googleapis/google-cloud-go/issues/11137)) ([19f01c3](https://github.com/googleapis/google-cloud-go/commit/19f01c3c48ed1272c8fc0af9e5f69646cb662808))
### Bug Fixes
* **storage:** Add backoff to gRPC write retries ([#11200](https://github.com/googleapis/google-cloud-go/issues/11200)) ([a7db927](https://github.com/googleapis/google-cloud-go/commit/a7db927da9cf4c6cf242a5db83e44a16d75a8291))
* **storage:** Correct direct connectivity check ([#11152](https://github.com/googleapis/google-cloud-go/issues/11152)) ([a75c8b0](https://github.com/googleapis/google-cloud-go/commit/a75c8b0f72c38d9a85c908715c3e37eb5cffb131))
* **storage:** Disable soft delete policy using 0 retentionDurationSeconds ([#11226](https://github.com/googleapis/google-cloud-go/issues/11226)) ([f087721](https://github.com/googleapis/google-cloud-go/commit/f087721b7b20ad28ded1d0a84756a8bbaa2bb95a))
* **storage:** Retry SignBlob call for URL signing ([#11154](https://github.com/googleapis/google-cloud-go/issues/11154)) ([f198452](https://github.com/googleapis/google-cloud-go/commit/f198452fd2b29e779e9080ba79d7e873eb0c32ef))
## [1.47.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.46.0...storage/v1.47.0) (2024-11-14)
### Features
* **storage:** Introduce dp detector based on grpc metrics ([#11100](https://github.com/googleapis/google-cloud-go/issues/11100)) ([60c2323](https://github.com/googleapis/google-cloud-go/commit/60c2323102b623e042fc508e2b1bb830a03f9577))
### Bug Fixes
* **storage:** Bump auth dep ([#11135](https://github.com/googleapis/google-cloud-go/issues/11135)) ([9620a51](https://github.com/googleapis/google-cloud-go/commit/9620a51b2c6904d8d93e124494bc297fb98553d2))
## [1.46.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.45.0...storage/v1.46.0) (2024-10-31)
### Features
* **storage:** Add grpc metrics experimental options ([#10984](https://github.com/googleapis/google-cloud-go/issues/10984)) ([5b7397b](https://github.com/googleapis/google-cloud-go/commit/5b7397b169176f030049e1511859a883422c774e))
### Bug Fixes
* **storage:** Skip only specific transport tests. ([#11016](https://github.com/googleapis/google-cloud-go/issues/11016)) ([d40fbff](https://github.com/googleapis/google-cloud-go/commit/d40fbff9c1984aeed0224a4ac93eb95c5af17126))
* **storage:** Update google.golang.org/api to v0.203.0 ([8bb87d5](https://github.com/googleapis/google-cloud-go/commit/8bb87d56af1cba736e0fe243979723e747e5e11e))
* **storage:** WARNING: On approximately Dec 1, 2024, an update to Protobuf will change service registration function signatures to use an interface instead of a concrete type in generated .pb.go files. This change is expected to affect very few if any users of this client library. For more information, see https://togithub.com/googleapis/google-cloud-go/issues/11020. ([2b8ca4b](https://github.com/googleapis/google-cloud-go/commit/2b8ca4b4127ce3025c7a21cc7247510e07cc5625))
### Miscellaneous Chores
* **storage/internal:** Remove notification, service account, and hmac RPCS. These API have been migrated to Storage Control and are available via the JSON API. ([#11008](https://github.com/googleapis/google-cloud-go/issues/11008)) ([e0759f4](https://github.com/googleapis/google-cloud-go/commit/e0759f46639b4c542e5b49e4dc81340d8e123370))
## [1.45.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.44.0...storage/v1.45.0) (2024-10-17)
### Features
* **storage/internal:** Adds support for restore token ([70d82fe](https://github.com/googleapis/google-cloud-go/commit/70d82fe93f60f1075298a077ce1616f9ae7e13fe))
* **storage:** Adding bucket-specific dynamicDelay ([#10987](https://github.com/googleapis/google-cloud-go/issues/10987)) ([a807a7e](https://github.com/googleapis/google-cloud-go/commit/a807a7e7f9fb002374407622c126102c5e61af82))
* **storage:** Dynamic read request stall timeout ([#10958](https://github.com/googleapis/google-cloud-go/issues/10958)) ([a09f00e](https://github.com/googleapis/google-cloud-go/commit/a09f00eeecac82af98ae769bab284ee58a3a66cb))
### Documentation
* **storage:** Remove preview wording from NewGRPCClient ([#11002](https://github.com/googleapis/google-cloud-go/issues/11002)) ([40c3a5b](https://github.com/googleapis/google-cloud-go/commit/40c3a5b9c4cd4db2f1695e180419197b6a03ed7f))
## [1.44.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.43.0...storage/v1.44.0) (2024-10-03)

View file

@ -326,11 +326,14 @@ func (b *BucketHandle) defaultSignBytesFunc(email string) func([]byte) ([]byte,
if err != nil {
return nil, fmt.Errorf("unable to create iamcredentials client: %w", err)
}
resp, err := svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{
Payload: base64.StdEncoding.EncodeToString(in),
}).Do()
if err != nil {
// Do the SignBlob call with a retry for transient errors.
var resp *iamcredentials.SignBlobResponse
if err := run(ctx, func(ctx context.Context) error {
resp, err = svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{
Payload: base64.StdEncoding.EncodeToString(in),
}).Do()
return err
}, b.retry, true); err != nil {
return nil, fmt.Errorf("unable to sign bytes: %w", err)
}
out, err := base64.StdEncoding.DecodeString(resp.SignedBlob)
@ -1338,8 +1341,10 @@ func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket {
}
if ua.SoftDeletePolicy != nil {
if ua.SoftDeletePolicy.RetentionDuration == 0 {
rb.NullFields = append(rb.NullFields, "SoftDeletePolicy")
rb.SoftDeletePolicy = nil
rb.SoftDeletePolicy = &raw.BucketSoftDeletePolicy{
RetentionDurationSeconds: 0,
ForceSendFields: []string{"RetentionDurationSeconds"},
}
} else {
rb.SoftDeletePolicy = ua.SoftDeletePolicy.toRawSoftDeletePolicy()
}

View file

@ -62,6 +62,7 @@ type storageClient interface {
GetObject(ctx context.Context, params *getObjectParams, opts ...storageOption) (*ObjectAttrs, error)
UpdateObject(ctx context.Context, params *updateObjectParams, opts ...storageOption) (*ObjectAttrs, error)
RestoreObject(ctx context.Context, params *restoreObjectParams, opts ...storageOption) (*ObjectAttrs, error)
MoveObject(ctx context.Context, params *moveObjectParams, opts ...storageOption) (*ObjectAttrs, error)
// Default Object ACL methods.
@ -107,6 +108,8 @@ type storageClient interface {
ListNotifications(ctx context.Context, bucket string, opts ...storageOption) (map[string]*Notification, error)
CreateNotification(ctx context.Context, bucket string, n *Notification, opts ...storageOption) (*Notification, error)
DeleteNotification(ctx context.Context, bucket string, id string, opts ...storageOption) error
NewMultiRangeDownloader(ctx context.Context, params *newMultiRangeDownloaderParams, opts ...storageOption) (*MultiRangeDownloader, error)
}
// settings contains transport-agnostic configuration for API calls made via
@ -237,7 +240,8 @@ type openWriterParams struct {
chunkSize int
// chunkRetryDeadline - see `Writer.ChunkRetryDeadline`.
// Optional.
chunkRetryDeadline time.Duration
chunkRetryDeadline time.Duration
chunkTransferTimeout time.Duration
// Object/request properties
@ -259,6 +263,9 @@ type openWriterParams struct {
// sendCRC32C - see `Writer.SendCRC32C`.
// Optional.
sendCRC32C bool
// append - Write with appendable object semantics.
// Optional.
append bool
// Writer callbacks
@ -276,6 +283,15 @@ type openWriterParams struct {
setObj func(*ObjectAttrs)
}
type newMultiRangeDownloaderParams struct {
bucket string
conds *Conditions
encryptionKey []byte
gen int64
object string
handle *ReadHandle
}
type newRangeReaderParams struct {
bucket string
conds *Conditions
@ -285,6 +301,7 @@ type newRangeReaderParams struct {
object string
offset int64
readCompressed bool // Use accept-encoding: gzip. Only works for HTTP currently.
handle *ReadHandle
}
type getObjectParams struct {
@ -312,6 +329,13 @@ type restoreObjectParams struct {
copySourceACL bool
}
type moveObjectParams struct {
bucket, srcObject, dstObject string
srcConds *Conditions
dstConds *Conditions
encryptionKey []byte
}
type composeObjectRequest struct {
dstBucket string
dstObject destinationObject

View file

@ -36,6 +36,23 @@ type dynamicDelay struct {
mu *sync.RWMutex
}
// validateDynamicDelayParams ensures,
// targetPercentile is a valid fraction (between 0 and 1).
// increaseRate is a positive number.
// minDelay is less than maxDelay.
func validateDynamicDelayParams(targetPercentile, increaseRate float64, minDelay, maxDelay time.Duration) error {
if targetPercentile < 0 || targetPercentile > 1 {
return fmt.Errorf("invalid targetPercentile (%v): must be within [0, 1]", targetPercentile)
}
if increaseRate <= 0 {
return fmt.Errorf("invalid increaseRate (%v): must be > 0", increaseRate)
}
if minDelay >= maxDelay {
return fmt.Errorf("invalid minDelay (%v) and maxDelay (%v) combination: minDelay must be smaller than maxDelay", minDelay, maxDelay)
}
return nil
}
// NewDynamicDelay returns a dynamicDelay.
//
// targetPercentile is the desired percentile to be computed. For example, a
@ -49,16 +66,7 @@ type dynamicDelay struct {
//
// decrease can never lower the delay past minDelay, increase can never raise
// the delay past maxDelay.
func newDynamicDelay(targetPercentile float64, increaseRate float64, initialDelay, minDelay, maxDelay time.Duration) (*dynamicDelay, error) {
if targetPercentile < 0 || targetPercentile > 1 {
return nil, fmt.Errorf("invalid targetPercentile (%v): must be within [0, 1]", targetPercentile)
}
if increaseRate <= 0 {
return nil, fmt.Errorf("invalid increaseRate (%v): must be > 0", increaseRate)
}
if minDelay >= maxDelay {
return nil, fmt.Errorf("invalid minDelay (%v) and maxDelay (%v) combination: minDelay must be smaller than maxDelay", minDelay, maxDelay)
}
func newDynamicDelay(targetPercentile float64, increaseRate float64, initialDelay, minDelay, maxDelay time.Duration) *dynamicDelay {
if initialDelay < minDelay {
initialDelay = minDelay
}
@ -84,7 +92,7 @@ func newDynamicDelay(targetPercentile float64, increaseRate float64, initialDela
maxDelay: maxDelay,
value: initialDelay,
mu: &sync.RWMutex{},
}, nil
}
}
func (d *dynamicDelay) unsafeIncrease() {
@ -141,7 +149,7 @@ func (d *dynamicDelay) getValue() time.Duration {
return d.value
}
// PrintDelay prints the state of delay, helpful in debugging.
// printDelay prints the state of delay, helpful in debugging.
func (d *dynamicDelay) printDelay() {
d.mu.RLock()
defer d.mu.RUnlock()
@ -152,3 +160,78 @@ func (d *dynamicDelay) printDelay() {
fmt.Println("MaxDelay: ", d.maxDelay)
fmt.Println("Value: ", d.value)
}
// bucketDelayManager wraps dynamicDelay to provide bucket-specific delays.
type bucketDelayManager struct {
targetPercentile float64
increaseRate float64
initialDelay time.Duration
minDelay time.Duration
maxDelay time.Duration
// delays maps bucket names to their dynamic delay instance.
delays map[string]*dynamicDelay
// mu guards delays.
mu *sync.RWMutex
}
// newBucketDelayManager returns a new bucketDelayManager instance.
func newBucketDelayManager(targetPercentile float64, increaseRate float64, initialDelay, minDelay, maxDelay time.Duration) (*bucketDelayManager, error) {
err := validateDynamicDelayParams(targetPercentile, increaseRate, minDelay, maxDelay)
if err != nil {
return nil, err
}
return &bucketDelayManager{
targetPercentile: targetPercentile,
increaseRate: increaseRate,
initialDelay: initialDelay,
minDelay: minDelay,
maxDelay: maxDelay,
delays: make(map[string]*dynamicDelay),
mu: &sync.RWMutex{},
}, nil
}
// getDelay retrieves the dynamicDelay instance for the given bucket name. If no delay
// exists for the bucket, a new one is created with the configured parameters.
func (b *bucketDelayManager) getDelay(bucketName string) *dynamicDelay {
b.mu.RLock()
delay, ok := b.delays[bucketName]
b.mu.RUnlock()
if !ok {
b.mu.Lock()
defer b.mu.Unlock()
// Check again, as someone might create b/w the execution of mu.RUnlock() and mu.Lock().
delay, ok = b.delays[bucketName]
if !ok {
// Create a new dynamicDelay for the bucket if it doesn't exist
delay = newDynamicDelay(b.targetPercentile, b.increaseRate, b.initialDelay, b.minDelay, b.maxDelay)
b.delays[bucketName] = delay
}
}
return delay
}
// increase notes that the operation took longer than the delay for the given bucket.
func (b *bucketDelayManager) increase(bucketName string) {
b.getDelay(bucketName).increase()
}
// decrease notes that the operation completed before the delay for the given bucket.
func (b *bucketDelayManager) decrease(bucketName string) {
b.getDelay(bucketName).decrease()
}
// update updates the delay value for the bucket depending on the specified latency.
func (b *bucketDelayManager) update(bucketName string, latency time.Duration) {
b.getDelay(bucketName).update(latency)
}
// getValue returns the desired delay to wait before retrying the operation for the given bucket.
func (b *bucketDelayManager) getValue(bucketName string) time.Duration {
return b.getDelay(bucketName).getValue()
}

View file

@ -89,4 +89,4 @@ then
fi
# Run tests
go test -v -timeout 10m ./ -run="^Test(RetryConformance|.*Emulated)$" -short 2>&1 | tee -a sponge_log.log
go test -v -timeout 15m ./ ./dataflux -run="^Test(RetryConformance|.*Emulated)$" -short 2>&1 | tee -a sponge_log.log

View file

@ -0,0 +1,87 @@
// Copyright 2024 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 experimental is a collection of experimental features that might
// have some rough edges to them. Housing experimental features in this package
// results in a user accessing these APIs as `experimental.Foo`, thereby making
// it explicit that the feature is experimental and using them in production
// code is at their own risk.
//
// All APIs in this package are experimental.
package experimental
import (
"time"
"cloud.google.com/go/storage/internal"
"go.opentelemetry.io/otel/sdk/metric"
"google.golang.org/api/option"
)
// WithMetricInterval provides a [option.ClientOption] that may be passed to [storage.NewGRPCClient].
// It sets how often to emit metrics [metric.WithInterval] when using
// [metric.NewPeriodicReader]
// When using Cloud Monitoring interval must be at minimum 1 [time.Minute].
func WithMetricInterval(metricInterval time.Duration) option.ClientOption {
return internal.WithMetricInterval.(func(time.Duration) option.ClientOption)(metricInterval)
}
// WithMetricExporter provides a [option.ClientOption] that may be passed to [storage.NewGRPCClient].
// Set an alternate client-side metric Exporter to emit metrics through.
// Must implement [metric.Exporter]
func WithMetricExporter(ex *metric.Exporter) option.ClientOption {
return internal.WithMetricExporter.(func(*metric.Exporter) option.ClientOption)(ex)
}
// WithReadStallTimeout provides a [option.ClientOption] that may be passed to [storage.NewClient].
// It enables the client to retry stalled requests when starting a download from
// Cloud Storage. If the timeout elapses with no response from the server, the request
// is automatically retried.
// The timeout is initially set to ReadStallTimeoutConfig.Min. The client tracks
// latency across all read requests from the client for each bucket accessed, and can
// adjust the timeout higher to the target percentile when latency for request to that
// bucket is high.
// Currently, this is supported only for downloads ([storage.NewReader] and
// [storage.NewRangeReader] calls) and only for the XML API. Other read APIs (gRPC & JSON)
// will be supported soon.
func WithReadStallTimeout(rstc *ReadStallTimeoutConfig) option.ClientOption {
return internal.WithReadStallTimeout.(func(config *ReadStallTimeoutConfig) option.ClientOption)(rstc)
}
// ReadStallTimeoutConfig defines the timeout which is adjusted dynamically based on
// past observed latencies.
type ReadStallTimeoutConfig struct {
// Min is the minimum duration of the timeout. The default value is 500ms. Requests
// taking shorter than this value to return response headers will never time out.
// In general, you should choose a Min value that is greater than the typical value
// for the target percentile.
Min time.Duration
// TargetPercentile is the percentile to target for the dynamic timeout. The default
// value is 0.99. At the default percentile, at most 1% of requests will be timed out
// and retried.
TargetPercentile float64
}
// WithGRPCBidiReads provides an [option.ClientOption] that may be passed to
// [cloud.google.com/go/storage.NewGRPCClient].
// It enables the client to use bi-directional gRPC APIs for downloads rather than the
// server streaming API. In particular, it allows users to use the [storage.MultiRangeDownloader]
// surface, which requires bi-directional streaming.
//
// The bi-directional API is in private preview; please contact your account manager if
// interested.
func WithGRPCBidiReads() option.ClientOption {
return internal.WithGRPCBidiReads.(func() option.ClientOption)()
}

File diff suppressed because it is too large Load diff

View file

@ -16,8 +16,8 @@ package storage
import (
"context"
"errors"
"fmt"
"log"
"strings"
"time"
@ -29,8 +29,8 @@ import (
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.opentelemetry.io/otel/sdk/resource"
"google.golang.org/api/option"
"google.golang.org/api/transport"
"google.golang.org/grpc"
"google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/stats/opentelemetry"
)
@ -39,6 +39,196 @@ const (
metricPrefix = "storage.googleapis.com/client/"
)
// Added to help with tests
type storageMonitoredResource struct {
project string
api string
location string
instance string
cloudPlatform string
host string
resource *resource.Resource
}
func (smr *storageMonitoredResource) exporter() (metric.Exporter, error) {
exporter, err := mexporter.New(
mexporter.WithProjectID(smr.project),
mexporter.WithMetricDescriptorTypeFormatter(metricFormatter),
mexporter.WithCreateServiceTimeSeries(),
mexporter.WithMonitoredResourceDescription(monitoredResourceName, []string{"project_id", "location", "cloud_platform", "host_id", "instance_id", "api"}),
)
if err != nil {
return nil, fmt.Errorf("storage: creating metrics exporter: %w", err)
}
return exporter, nil
}
func newStorageMonitoredResource(ctx context.Context, project, api string, opts ...resource.Option) (*storageMonitoredResource, error) {
detectedAttrs, err := resource.New(ctx, opts...)
if err != nil {
return nil, err
}
smr := &storageMonitoredResource{
instance: uuid.New().String(),
api: api,
project: project,
}
s := detectedAttrs.Set()
// Attempt to use resource detector project id if project id wasn't
// identified using ADC as a last resort. Otherwise metrics cannot be started.
if p, present := s.Value("cloud.account.id"); present && smr.project == "" {
smr.project = p.AsString()
} else if !present && smr.project == "" {
return nil, errors.New("google cloud project is required to start client-side metrics")
}
if v, ok := s.Value("cloud.region"); ok {
smr.location = v.AsString()
} else {
smr.location = "global"
}
if v, ok := s.Value("cloud.platform"); ok {
smr.cloudPlatform = v.AsString()
} else {
smr.cloudPlatform = "unknown"
}
if v, ok := s.Value("host.id"); ok {
smr.host = v.AsString()
} else if v, ok := s.Value("faas.id"); ok {
smr.host = v.AsString()
} else {
smr.host = "unknown"
}
smr.resource, err = resource.New(ctx, resource.WithAttributes([]attribute.KeyValue{
{Key: "gcp.resource_type", Value: attribute.StringValue(monitoredResourceName)},
{Key: "project_id", Value: attribute.StringValue(smr.project)},
{Key: "api", Value: attribute.StringValue(smr.api)},
{Key: "instance_id", Value: attribute.StringValue(smr.instance)},
{Key: "location", Value: attribute.StringValue(smr.location)},
{Key: "cloud_platform", Value: attribute.StringValue(smr.cloudPlatform)},
{Key: "host_id", Value: attribute.StringValue(smr.host)},
}...))
if err != nil {
return nil, err
}
return smr, nil
}
type metricsContext struct {
// client options passed to gRPC channels
clientOpts []option.ClientOption
// instance of metric reader used by gRPC client-side metrics
provider *metric.MeterProvider
// clean func to call when closing gRPC client
close func()
}
type metricsConfig struct {
project string
interval time.Duration
customExporter *metric.Exporter
manualReader *metric.ManualReader // used by tests
disableExporter bool // used by tests disables exports
resourceOpts []resource.Option // used by tests
}
func newGRPCMetricContext(ctx context.Context, cfg metricsConfig) (*metricsContext, error) {
var exporter metric.Exporter
meterOpts := []metric.Option{}
if cfg.customExporter == nil {
var ropts []resource.Option
if cfg.resourceOpts != nil {
ropts = cfg.resourceOpts
} else {
ropts = []resource.Option{resource.WithDetectors(gcp.NewDetector())}
}
smr, err := newStorageMonitoredResource(ctx, cfg.project, "grpc", ropts...)
if err != nil {
return nil, err
}
exporter, err = smr.exporter()
if err != nil {
return nil, err
}
meterOpts = append(meterOpts, metric.WithResource(smr.resource))
} else {
exporter = *cfg.customExporter
}
interval := time.Minute
if cfg.interval > 0 {
interval = cfg.interval
}
meterOpts = append(meterOpts,
// Metric views update histogram boundaries to be relevant to GCS
// otherwise default OTel histogram boundaries are used.
metric.WithView(
createHistogramView("grpc.client.attempt.duration", latencyHistogramBoundaries()),
createHistogramView("grpc.client.attempt.rcvd_total_compressed_message_size", sizeHistogramBoundaries()),
createHistogramView("grpc.client.attempt.sent_total_compressed_message_size", sizeHistogramBoundaries())),
)
if cfg.manualReader != nil {
meterOpts = append(meterOpts, metric.WithReader(cfg.manualReader))
}
if !cfg.disableExporter {
meterOpts = append(meterOpts, metric.WithReader(
metric.NewPeriodicReader(&exporterLogSuppressor{Exporter: exporter}, metric.WithInterval(interval))))
}
provider := metric.NewMeterProvider(meterOpts...)
mo := opentelemetry.MetricsOptions{
MeterProvider: provider,
Metrics: stats.NewMetrics(
"grpc.client.attempt.started",
"grpc.client.attempt.duration",
"grpc.client.attempt.sent_total_compressed_message_size",
"grpc.client.attempt.rcvd_total_compressed_message_size",
"grpc.client.call.duration",
"grpc.lb.wrr.rr_fallback",
"grpc.lb.wrr.endpoint_weight_not_yet_usable",
"grpc.lb.wrr.endpoint_weight_stale",
"grpc.lb.wrr.endpoint_weights",
"grpc.lb.rls.cache_entries",
"grpc.lb.rls.cache_size",
"grpc.lb.rls.default_target_picks",
"grpc.lb.rls.target_picks",
"grpc.lb.rls.failed_picks",
),
OptionalLabels: []string{"grpc.lb.locality"},
}
opts := []option.ClientOption{
option.WithGRPCDialOption(
opentelemetry.DialOption(opentelemetry.Options{MetricsOptions: mo})),
option.WithGRPCDialOption(
grpc.WithDefaultCallOptions(grpc.StaticMethodCallOption{})),
}
return &metricsContext{
clientOpts: opts,
provider: provider,
close: func() {
provider.Shutdown(ctx)
},
}, nil
}
// Silences permission errors after initial error is emitted to prevent
// chatty logs.
type exporterLogSuppressor struct {
metric.Exporter
emittedFailure bool
}
// Implements OTel SDK metric.Exporter interface to prevent noisy logs from
// lack of credentials after initial failure.
// https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric@v1.28.0#Exporter
func (e *exporterLogSuppressor) Export(ctx context.Context, rm *metricdata.ResourceMetrics) error {
if err := e.Exporter.Export(ctx, rm); err != nil && !e.emittedFailure {
if strings.Contains(err.Error(), "PermissionDenied") {
e.emittedFailure = true
return fmt.Errorf("gRPC metrics failed due permission issue: %w", err)
}
return err
}
return nil
}
func latencyHistogramBoundaries() []float64 {
boundaries := []float64{}
boundary := 0.0
@ -78,72 +268,6 @@ func sizeHistogramBoundaries() []float64 {
return boundaries
}
func metricFormatter(m metricdata.Metrics) string {
return metricPrefix + strings.ReplaceAll(string(m.Name), ".", "/")
}
func gcpAttributeExpectedDefaults() []attribute.KeyValue {
return []attribute.KeyValue{
{Key: "location", Value: attribute.StringValue("global")},
{Key: "cloud_platform", Value: attribute.StringValue("unknown")},
{Key: "host_id", Value: attribute.StringValue("unknown")}}
}
// Added to help with tests
type preparedResource struct {
projectToUse string
resource *resource.Resource
}
func newPreparedResource(ctx context.Context, project string, resourceOptions []resource.Option) (*preparedResource, error) {
detectedAttrs, err := resource.New(ctx, resourceOptions...)
if err != nil {
return nil, err
}
preparedResource := &preparedResource{}
s := detectedAttrs.Set()
p, present := s.Value("cloud.account.id")
if present {
preparedResource.projectToUse = p.AsString()
} else {
preparedResource.projectToUse = project
}
updates := []attribute.KeyValue{}
for _, kv := range gcpAttributeExpectedDefaults() {
if val, present := s.Value(kv.Key); !present || val.AsString() == "" {
updates = append(updates, attribute.KeyValue{Key: kv.Key, Value: kv.Value})
}
}
r, err := resource.New(
ctx,
resource.WithAttributes(
attribute.KeyValue{Key: "gcp.resource_type", Value: attribute.StringValue(monitoredResourceName)},
attribute.KeyValue{Key: "instance_id", Value: attribute.StringValue(uuid.New().String())},
attribute.KeyValue{Key: "project_id", Value: attribute.StringValue(project)},
attribute.KeyValue{Key: "api", Value: attribute.StringValue("grpc")},
),
resource.WithAttributes(detectedAttrs.Attributes()...),
// Last duplicate key / value wins
resource.WithAttributes(updates...),
)
if err != nil {
return nil, err
}
preparedResource.resource = r
return preparedResource, nil
}
type metricsContext struct {
// project used by exporter
project string
// client options passed to gRPC channels
clientOpts []option.ClientOption
// instance of metric reader used by gRPC client-side metrics
provider *metric.MeterProvider
// clean func to call when closing gRPC client
close func()
}
func createHistogramView(name string, boundaries []float64) metric.View {
return metric.NewView(metric.Instrument{
Name: name,
@ -154,122 +278,6 @@ func createHistogramView(name string, boundaries []float64) metric.View {
})
}
func newGRPCMetricContext(ctx context.Context, project string) (*metricsContext, error) {
preparedResource, err := newPreparedResource(ctx, project, []resource.Option{resource.WithDetectors(gcp.NewDetector())})
if err != nil {
return nil, err
}
// Implementation requires a project, if one is not determined possibly user
// credentials. Then we will fail stating gRPC Metrics require a project-id.
if project == "" && preparedResource.projectToUse != "" {
return nil, fmt.Errorf("google cloud project is required to start client-side metrics")
}
// If projectTouse isn't the same as project provided to Storage client, then
// emit a log stating which project is being used to emit metrics to.
if project != preparedResource.projectToUse {
log.Printf("The Project ID configured for metrics is %s, but the Project ID of the storage client is %s. Make sure that the service account in use has the required metric writing role (roles/monitoring.metricWriter) in the project projectIdToUse or metrics will not be written.", preparedResource.projectToUse, project)
}
meOpts := []mexporter.Option{
mexporter.WithProjectID(preparedResource.projectToUse),
mexporter.WithMetricDescriptorTypeFormatter(metricFormatter),
mexporter.WithCreateServiceTimeSeries(),
mexporter.WithMonitoredResourceDescription(monitoredResourceName, []string{"project_id", "location", "cloud_platform", "host_id", "instance_id", "api"})}
exporter, err := mexporter.New(meOpts...)
if err != nil {
return nil, err
}
// Metric views update histogram boundaries to be relevant to GCS
// otherwise default OTel histogram boundaries are used.
metricViews := []metric.View{
createHistogramView("grpc.client.attempt.duration", latencyHistogramBoundaries()),
createHistogramView("grpc.client.attempt.rcvd_total_compressed_message_size", sizeHistogramBoundaries()),
createHistogramView("grpc.client.attempt.sent_total_compressed_message_size", sizeHistogramBoundaries()),
}
provider := metric.NewMeterProvider(
metric.WithReader(metric.NewPeriodicReader(&exporterLogSuppressor{exporter: exporter}, metric.WithInterval(time.Minute))),
metric.WithResource(preparedResource.resource),
metric.WithView(metricViews...),
)
mo := opentelemetry.MetricsOptions{
MeterProvider: provider,
Metrics: opentelemetry.DefaultMetrics().Add(
"grpc.lb.wrr.rr_fallback",
"grpc.lb.wrr.endpoint_weight_not_yet_usable",
"grpc.lb.wrr.endpoint_weight_stale",
"grpc.lb.wrr.endpoint_weights",
"grpc.lb.rls.cache_entries",
"grpc.lb.rls.cache_size",
"grpc.lb.rls.default_target_picks",
"grpc.lb.rls.target_picks",
"grpc.lb.rls.failed_picks"),
OptionalLabels: []string{"grpc.lb.locality"},
}
opts := []option.ClientOption{
option.WithGRPCDialOption(opentelemetry.DialOption(opentelemetry.Options{MetricsOptions: mo})),
option.WithGRPCDialOption(grpc.WithDefaultCallOptions(grpc.StaticMethodCallOption{})),
}
context := &metricsContext{
project: preparedResource.projectToUse,
clientOpts: opts,
provider: provider,
close: createShutdown(ctx, provider),
}
return context, nil
}
func enableClientMetrics(ctx context.Context, s *settings) (*metricsContext, error) {
var project string
c, err := transport.Creds(ctx, s.clientOption...)
if err == nil {
project = c.ProjectID
}
// Enable client-side metrics for gRPC
metricsContext, err := newGRPCMetricContext(ctx, project)
if err != nil {
return nil, fmt.Errorf("gRPC Metrics: %w", err)
}
return metricsContext, nil
}
func createShutdown(ctx context.Context, provider *metric.MeterProvider) func() {
return func() {
provider.Shutdown(ctx)
}
}
// Silences permission errors after initial error is emitted to prevent
// chatty logs.
type exporterLogSuppressor struct {
exporter metric.Exporter
emittedFailure bool
}
// Implements OTel SDK metric.Exporter interface to prevent noisy logs from
// lack of credentials after initial failure.
// https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric@v1.28.0#Exporter
func (e *exporterLogSuppressor) Export(ctx context.Context, rm *metricdata.ResourceMetrics) error {
if err := e.exporter.Export(ctx, rm); err != nil && !e.emittedFailure {
if strings.Contains(err.Error(), "PermissionDenied") {
e.emittedFailure = true
return fmt.Errorf("gRPC metrics failed due permission issue: %w", err)
}
return err
}
return nil
}
func (e *exporterLogSuppressor) Temporality(k metric.InstrumentKind) metricdata.Temporality {
return e.exporter.Temporality(k)
}
func (e *exporterLogSuppressor) Aggregation(k metric.InstrumentKind) metric.Aggregation {
return e.exporter.Aggregation(k)
}
func (e *exporterLogSuppressor) ForceFlush(ctx context.Context) error {
return e.exporter.ForceFlush(ctx)
}
func (e *exporterLogSuppressor) Shutdown(ctx context.Context) error {
return e.exporter.Shutdown(ctx)
func metricFormatter(m metricdata.Metrics) string {
return metricPrefix + strings.ReplaceAll(string(m.Name), ".", "/")
}

870
vendor/cloud.google.com/go/storage/grpc_reader.go generated vendored Normal file
View file

@ -0,0 +1,870 @@
// Copyright 2025 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 storage
import (
"context"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"io"
"cloud.google.com/go/internal/trace"
"cloud.google.com/go/storage/internal/apiv2/storagepb"
"github.com/googleapis/gax-go/v2"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/mem"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/proto"
)
// Below is the legacy implementation of gRPC downloads using the ReadObject API.
// It's used by gRPC if the experimental option WithGRPCBidiReads was not passed.
// TODO: once BidiReadObject is in GA, remove this implementation.
// Custom codec to be used for unmarshaling ReadObjectResponse messages.
// This is used to avoid a copy of object data in proto.Unmarshal.
type bytesCodecReadObject struct {
}
var _ encoding.CodecV2 = bytesCodecReadObject{}
// Marshal is used to encode messages to send for bytesCodecReadObject. Since we are only
// using this to send ReadObjectRequest messages we don't need to recycle buffers
// here.
func (bytesCodecReadObject) Marshal(v any) (mem.BufferSlice, error) {
vv, ok := v.(proto.Message)
if !ok {
return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v)
}
var data mem.BufferSlice
buf, err := proto.Marshal(vv)
if err != nil {
return nil, err
}
data = append(data, mem.SliceBuffer(buf))
return data, nil
}
// Unmarshal is used for data received for ReadObjectResponse. We want to preserve
// the mem.BufferSlice in most cases rather than copying and calling proto.Unmarshal.
func (bytesCodecReadObject) Unmarshal(data mem.BufferSlice, v any) error {
switch v := v.(type) {
case *mem.BufferSlice:
*v = data
// Pick up a reference to the data so that it is not freed while decoding.
data.Ref()
return nil
case proto.Message:
buf := data.MaterializeToBuffer(mem.DefaultBufferPool())
return proto.Unmarshal(buf.ReadOnlyData(), v)
default:
return fmt.Errorf("cannot unmarshal type %T, want proto.Message or mem.BufferSlice", v)
}
}
func (bytesCodecReadObject) Name() string {
return ""
}
func (c *grpcStorageClient) NewRangeReaderReadObject(ctx context.Context, params *newRangeReaderParams, opts ...storageOption) (r *Reader, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.grpcStorageClient.NewRangeReaderReadObject")
defer func() { trace.EndSpan(ctx, err) }()
s := callSettings(c.settings, opts...)
s.gax = append(s.gax, gax.WithGRPCOptions(
grpc.ForceCodecV2(bytesCodecReadObject{}),
))
if s.userProject != "" {
ctx = setUserProjectMetadata(ctx, s.userProject)
}
b := bucketResourceName(globalProjectAlias, params.bucket)
req := &storagepb.ReadObjectRequest{
Bucket: b,
Object: params.object,
CommonObjectRequestParams: toProtoCommonObjectRequestParams(params.encryptionKey),
}
// The default is a negative value, which means latest.
if params.gen >= 0 {
req.Generation = params.gen
}
// Define a function that initiates a Read with offset and length, assuming
// we have already read seen bytes.
reopen := func(seen int64) (*readStreamResponseReadObject, context.CancelFunc, error) {
// If the context has already expired, return immediately without making
// we call.
if err := ctx.Err(); err != nil {
return nil, nil, err
}
cc, cancel := context.WithCancel(ctx)
req.ReadOffset = params.offset + seen
// Only set a ReadLimit if length is greater than zero, because <= 0 means
// to read it all.
if params.length > 0 {
req.ReadLimit = params.length - seen
}
if err := applyCondsProto("gRPCReadObjectReader.reopen", params.gen, params.conds, req); err != nil {
cancel()
return nil, nil, err
}
var stream storagepb.Storage_ReadObjectClient
var err error
var decoder *readObjectResponseDecoder
err = run(cc, func(ctx context.Context) error {
stream, err = c.raw.ReadObject(ctx, req, s.gax...)
if err != nil {
return err
}
// Receive the message into databuf as a wire-encoded message so we can
// use a custom decoder to avoid an extra copy at the protobuf layer.
databufs := mem.BufferSlice{}
err := stream.RecvMsg(&databufs)
// These types of errors show up on the Recv call, rather than the
// initialization of the stream via ReadObject above.
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return ErrObjectNotExist
}
if err != nil {
return err
}
// Use a custom decoder that uses protobuf unmarshalling for all
// fields except the object data. Object data is handled separately
// to avoid a copy.
decoder = &readObjectResponseDecoder{
databufs: databufs,
}
err = decoder.readFullObjectResponse()
return err
}, s.retry, s.idempotent)
if err != nil {
// Close the stream context we just created to ensure we don't leak
// resources.
cancel()
// Free any buffers.
if decoder != nil && decoder.databufs != nil {
decoder.databufs.Free()
}
return nil, nil, err
}
return &readStreamResponseReadObject{stream, decoder}, cancel, nil
}
res, cancel, err := reopen(0)
if err != nil {
return nil, err
}
// The first message was Recv'd on stream open, use it to populate the
// object metadata.
msg := res.decoder.msg
obj := msg.GetMetadata()
// This is the size of the entire object, even if only a range was requested.
size := obj.GetSize()
// Only support checksums when reading an entire object, not a range.
var (
wantCRC uint32
checkCRC bool
)
if checksums := msg.GetObjectChecksums(); checksums != nil && checksums.Crc32C != nil {
if params.offset == 0 && params.length < 0 {
checkCRC = true
}
wantCRC = checksums.GetCrc32C()
}
metadata := obj.GetMetadata()
r = &Reader{
Attrs: ReaderObjectAttrs{
Size: size,
ContentType: obj.GetContentType(),
ContentEncoding: obj.GetContentEncoding(),
CacheControl: obj.GetCacheControl(),
LastModified: obj.GetUpdateTime().AsTime(),
Metageneration: obj.GetMetageneration(),
Generation: obj.GetGeneration(),
CRC32C: wantCRC,
},
objectMetadata: &metadata,
reader: &gRPCReadObjectReader{
stream: res.stream,
reopen: reopen,
cancel: cancel,
size: size,
// Preserve the decoder to read out object data when Read/WriteTo is called.
currMsg: res.decoder,
settings: s,
zeroRange: params.length == 0,
wantCRC: wantCRC,
checkCRC: checkCRC,
},
checkCRC: checkCRC,
}
cr := msg.GetContentRange()
if cr != nil {
r.Attrs.StartOffset = cr.GetStart()
r.remain = cr.GetEnd() - cr.GetStart()
} else {
r.remain = size
}
// For a zero-length request, explicitly close the stream and set remaining
// bytes to zero.
if params.length == 0 {
r.remain = 0
r.reader.Close()
}
return r, nil
}
type readStreamResponseReadObject struct {
stream storagepb.Storage_ReadObjectClient
decoder *readObjectResponseDecoder
}
type gRPCReadObjectReader struct {
seen, size int64
zeroRange bool
stream storagepb.Storage_ReadObjectClient
reopen func(seen int64) (*readStreamResponseReadObject, context.CancelFunc, error)
leftovers []byte
currMsg *readObjectResponseDecoder // decoder for the current message
cancel context.CancelFunc
settings *settings
checkCRC bool // should we check the CRC?
wantCRC uint32 // the CRC32c value the server sent in the header
gotCRC uint32 // running crc
}
// Update the running CRC with the data in the slice, if CRC checking was enabled.
func (r *gRPCReadObjectReader) updateCRC(b []byte) {
if r.checkCRC {
r.gotCRC = crc32.Update(r.gotCRC, crc32cTable, b)
}
}
// Checks whether the CRC matches at the conclusion of a read, if CRC checking was enabled.
func (r *gRPCReadObjectReader) runCRCCheck() error {
if r.checkCRC && r.gotCRC != r.wantCRC {
return fmt.Errorf("storage: bad CRC on read: got %d, want %d", r.gotCRC, r.wantCRC)
}
return nil
}
// Read reads bytes into the user's buffer from an open gRPC stream.
func (r *gRPCReadObjectReader) Read(p []byte) (int, error) {
// The entire object has been read by this reader, check the checksum if
// necessary and return EOF.
if r.size == r.seen || r.zeroRange {
if err := r.runCRCCheck(); err != nil {
return 0, err
}
return 0, io.EOF
}
// No stream to read from, either never initialized or Close was called.
// Note: There is a potential concurrency issue if multiple routines are
// using the same reader. One encounters an error and the stream is closed
// and then reopened while the other routine attempts to read from it.
if r.stream == nil {
return 0, fmt.Errorf("storage: reader has been closed")
}
var n int
// If there is data remaining in the current message, return what was
// available to conform to the Reader
// interface: https://pkg.go.dev/io#Reader.
if !r.currMsg.done {
n = r.currMsg.readAndUpdateCRC(p, func(b []byte) {
r.updateCRC(b)
})
r.seen += int64(n)
return n, nil
}
// Attempt to Recv the next message on the stream.
// This will update r.currMsg with the decoder for the new message.
err := r.recv()
if err != nil {
return 0, err
}
// TODO: Determine if we need to capture incremental CRC32C for this
// chunk. The Object CRC32C checksum is captured when directed to read
// the entire Object. If directed to read a range, we may need to
// calculate the range's checksum for verification if the checksum is
// present in the response here.
// TODO: Figure out if we need to support decompressive transcoding
// https://cloud.google.com/storage/docs/transcoding.
n = r.currMsg.readAndUpdateCRC(p, func(b []byte) {
r.updateCRC(b)
})
r.seen += int64(n)
return n, nil
}
// WriteTo writes all the data requested by the Reader into w, implementing
// io.WriterTo.
func (r *gRPCReadObjectReader) WriteTo(w io.Writer) (int64, error) {
// The entire object has been read by this reader, check the checksum if
// necessary and return nil.
if r.size == r.seen || r.zeroRange {
if err := r.runCRCCheck(); err != nil {
return 0, err
}
return 0, nil
}
// No stream to read from, either never initialized or Close was called.
// Note: There is a potential concurrency issue if multiple routines are
// using the same reader. One encounters an error and the stream is closed
// and then reopened while the other routine attempts to read from it.
if r.stream == nil {
return 0, fmt.Errorf("storage: reader has been closed")
}
// Track bytes written during before call.
var alreadySeen = r.seen
// Write any already received message to the stream. There will be some leftovers from the
// original NewRangeReaderReadObject call.
if r.currMsg != nil && !r.currMsg.done {
written, err := r.currMsg.writeToAndUpdateCRC(w, func(b []byte) {
r.updateCRC(b)
})
r.seen += int64(written)
r.currMsg = nil
if err != nil {
return r.seen - alreadySeen, err
}
}
// Loop and receive additional messages until the entire data is written.
for {
// Attempt to receive the next message on the stream.
// Will terminate with io.EOF once data has all come through.
// recv() handles stream reopening and retry logic so no need for retries here.
err := r.recv()
if err != nil {
if err == io.EOF {
// We are done; check the checksum if necessary and return.
err = r.runCRCCheck()
}
return r.seen - alreadySeen, err
}
// TODO: Determine if we need to capture incremental CRC32C for this
// chunk. The Object CRC32C checksum is captured when directed to read
// the entire Object. If directed to read a range, we may need to
// calculate the range's checksum for verification if the checksum is
// present in the response here.
// TODO: Figure out if we need to support decompressive transcoding
// https://cloud.google.com/storage/docs/transcoding.
written, err := r.currMsg.writeToAndUpdateCRC(w, func(b []byte) {
r.updateCRC(b)
})
r.seen += int64(written)
if err != nil {
return r.seen - alreadySeen, err
}
}
}
// Close cancels the read stream's context in order for it to be closed and
// collected, and frees any currently in use buffers.
func (r *gRPCReadObjectReader) Close() error {
if r.cancel != nil {
r.cancel()
}
r.stream = nil
r.currMsg = nil
return nil
}
// recv attempts to Recv the next message on the stream and extract the object
// data that it contains. In the event that a retryable error is encountered,
// the stream will be closed, reopened, and RecvMsg again.
// This will attempt to Recv until one of the following is true:
//
// * Recv is successful
// * A non-retryable error is encountered
// * The Reader's context is canceled
//
// The last error received is the one that is returned, which could be from
// an attempt to reopen the stream.
func (r *gRPCReadObjectReader) recv() error {
databufs := mem.BufferSlice{}
err := r.stream.RecvMsg(&databufs)
var shouldRetry = ShouldRetry
if r.settings.retry != nil && r.settings.retry.shouldRetry != nil {
shouldRetry = r.settings.retry.shouldRetry
}
if err != nil && shouldRetry(err) {
// This will "close" the existing stream and immediately attempt to
// reopen the stream, but will backoff if further attempts are necessary.
// Reopening the stream Recvs the first message, so if retrying is
// successful, r.currMsg will be updated to include the new data.
return r.reopenStream()
}
if err != nil {
return err
}
r.currMsg = &readObjectResponseDecoder{databufs: databufs}
return r.currMsg.readFullObjectResponse()
}
// ReadObjectResponse field and subfield numbers.
const (
checksummedDataFieldReadObject = protowire.Number(1)
checksummedDataContentFieldReadObject = protowire.Number(1)
checksummedDataCRC32CFieldReadObject = protowire.Number(2)
objectChecksumsFieldReadObject = protowire.Number(2)
contentRangeFieldReadObject = protowire.Number(3)
metadataFieldReadObject = protowire.Number(4)
)
// readObjectResponseDecoder is a wrapper on the raw message, used to decode one message
// without copying object data. It also has methods to write out the resulting object
// data to the user application.
type readObjectResponseDecoder struct {
databufs mem.BufferSlice // raw bytes of the message being processed
// Decoding offsets
off uint64 // offset in the messsage relative to the data as a whole
currBuf int // index of the current buffer being processed
currOff uint64 // offset in the current buffer
// Processed data
msg *storagepb.ReadObjectResponse // processed response message with all fields other than object data populated
dataOffsets bufferSliceOffsetsReadObject // offsets of the object data in the message.
done bool // true if the data has been completely read.
}
type bufferSliceOffsetsReadObject struct {
startBuf, endBuf int // indices of start and end buffers of object data in the msg
startOff, endOff uint64 // offsets within these buffers where the data starts and ends.
currBuf int // index of current buffer being read out to the user application.
currOff uint64 // offset of read in current buffer.
}
// peek ahead 10 bytes from the current offset in the databufs. This will return a
// slice of the current buffer if the bytes are all in one buffer, but will copy
// the bytes into a new buffer if the distance is split across buffers. Use this
// to allow protowire methods to be used to parse tags & fixed values.
// The max length of a varint tag is 10 bytes, see
// https://protobuf.dev/programming-guides/encoding/#varints . Other int types
// are shorter.
func (d *readObjectResponseDecoder) peek() []byte {
b := d.databufs[d.currBuf].ReadOnlyData()
// Check if the tag will fit in the current buffer. If not, copy the next 10
// bytes into a new buffer to ensure that we can read the tag correctly
// without it being divided between buffers.
tagBuf := b[d.currOff:]
remainingInBuf := len(tagBuf)
// If we have less than 10 bytes remaining and are not in the final buffer,
// copy up to 10 bytes ahead from the next buffer.
if remainingInBuf < binary.MaxVarintLen64 && d.currBuf != len(d.databufs)-1 {
tagBuf = d.copyNextBytes(10)
}
return tagBuf
}
// Copies up to next n bytes into a new buffer, or fewer if fewer bytes remain in the
// buffers overall. Does not advance offsets.
func (d *readObjectResponseDecoder) copyNextBytes(n int) []byte {
remaining := n
if r := d.databufs.Len() - int(d.off); r < remaining {
remaining = r
}
currBuf := d.currBuf
currOff := d.currOff
var buf []byte
for remaining > 0 {
b := d.databufs[currBuf].ReadOnlyData()
remainingInCurr := len(b[currOff:])
if remainingInCurr < remaining {
buf = append(buf, b[currOff:]...)
remaining -= remainingInCurr
currBuf++
currOff = 0
} else {
buf = append(buf, b[currOff:currOff+uint64(remaining)]...)
remaining = 0
}
}
return buf
}
// Advance current buffer & byte offset in the decoding by n bytes. Returns an error if we
// go past the end of the data.
func (d *readObjectResponseDecoder) advanceOffset(n uint64) error {
remaining := n
for remaining > 0 {
remainingInCurr := uint64(d.databufs[d.currBuf].Len()) - d.currOff
if remainingInCurr <= remaining {
remaining -= remainingInCurr
d.currBuf++
d.currOff = 0
} else {
d.currOff += remaining
remaining = 0
}
}
// If we have advanced past the end of the buffers, something went wrong.
if (d.currBuf == len(d.databufs) && d.currOff > 0) || d.currBuf > len(d.databufs) {
return errors.New("decoding: truncated message, cannot advance offset")
}
d.off += n
return nil
}
// This copies object data from the message into the buffer and returns the number of
// bytes copied. The data offsets are incremented in the message. The updateCRC
// function is called on the copied bytes.
func (d *readObjectResponseDecoder) readAndUpdateCRC(p []byte, updateCRC func([]byte)) int {
// For a completely empty message, just return 0
if len(d.databufs) == 0 {
return 0
}
databuf := d.databufs[d.dataOffsets.currBuf]
startOff := d.dataOffsets.currOff
var b []byte
if d.dataOffsets.currBuf == d.dataOffsets.endBuf {
b = databuf.ReadOnlyData()[startOff:d.dataOffsets.endOff]
} else {
b = databuf.ReadOnlyData()[startOff:]
}
n := copy(p, b)
updateCRC(b[:n])
d.dataOffsets.currOff += uint64(n)
// We've read all the data from this message. Free the underlying buffers.
if d.dataOffsets.currBuf == d.dataOffsets.endBuf && d.dataOffsets.currOff == d.dataOffsets.endOff {
d.done = true
d.databufs.Free()
}
// We are at the end of the current buffer
if d.dataOffsets.currBuf != d.dataOffsets.endBuf && d.dataOffsets.currOff == uint64(databuf.Len()) {
d.dataOffsets.currOff = 0
d.dataOffsets.currBuf++
}
return n
}
func (d *readObjectResponseDecoder) writeToAndUpdateCRC(w io.Writer, updateCRC func([]byte)) (int64, error) {
// For a completely empty message, just return 0
if len(d.databufs) == 0 {
return 0, nil
}
var written int64
for !d.done {
databuf := d.databufs[d.dataOffsets.currBuf]
startOff := d.dataOffsets.currOff
var b []byte
if d.dataOffsets.currBuf == d.dataOffsets.endBuf {
b = databuf.ReadOnlyData()[startOff:d.dataOffsets.endOff]
} else {
b = databuf.ReadOnlyData()[startOff:]
}
var n int
// Write all remaining data from the current buffer
n, err := w.Write(b)
written += int64(n)
updateCRC(b)
if err != nil {
return written, err
}
d.dataOffsets.currOff = 0
// We've read all the data from this message.
if d.dataOffsets.currBuf == d.dataOffsets.endBuf {
d.done = true
d.databufs.Free()
} else {
d.dataOffsets.currBuf++
}
}
return written, nil
}
// Consume the next available tag in the input data and return the field number and type.
// Advances the relevant offsets in the data.
func (d *readObjectResponseDecoder) consumeTag() (protowire.Number, protowire.Type, error) {
tagBuf := d.peek()
// Consume the next tag. This will tell us which field is next in the
// buffer, its type, and how much space it takes up.
fieldNum, fieldType, tagLength := protowire.ConsumeTag(tagBuf)
if tagLength < 0 {
return 0, 0, protowire.ParseError(tagLength)
}
// Update the offsets and current buffer depending on the tag length.
if err := d.advanceOffset(uint64(tagLength)); err != nil {
return 0, 0, fmt.Errorf("consuming tag: %w", err)
}
return fieldNum, fieldType, nil
}
// Consume a varint that represents the length of a bytes field. Return the length of
// the data, and advance the offsets by the length of the varint.
func (d *readObjectResponseDecoder) consumeVarint() (uint64, error) {
tagBuf := d.peek()
// Consume the next tag. This will tell us which field is next in the
// buffer, its type, and how much space it takes up.
dataLength, tagLength := protowire.ConsumeVarint(tagBuf)
if tagLength < 0 {
return 0, protowire.ParseError(tagLength)
}
// Update the offsets and current buffer depending on the tag length.
d.advanceOffset(uint64(tagLength))
return dataLength, nil
}
func (d *readObjectResponseDecoder) consumeFixed32() (uint32, error) {
valueBuf := d.peek()
// Consume the next tag. This will tell us which field is next in the
// buffer, its type, and how much space it takes up.
value, tagLength := protowire.ConsumeFixed32(valueBuf)
if tagLength < 0 {
return 0, protowire.ParseError(tagLength)
}
// Update the offsets and current buffer depending on the tag length.
d.advanceOffset(uint64(tagLength))
return value, nil
}
func (d *readObjectResponseDecoder) consumeFixed64() (uint64, error) {
valueBuf := d.peek()
// Consume the next tag. This will tell us which field is next in the
// buffer, its type, and how much space it takes up.
value, tagLength := protowire.ConsumeFixed64(valueBuf)
if tagLength < 0 {
return 0, protowire.ParseError(tagLength)
}
// Update the offsets and current buffer depending on the tag length.
d.advanceOffset(uint64(tagLength))
return value, nil
}
// Consume any field values up to the end offset provided and don't return anything.
// This is used to skip any values which are not going to be used.
// msgEndOff is indexed in terms of the overall data across all buffers.
func (d *readObjectResponseDecoder) consumeFieldValue(fieldNum protowire.Number, fieldType protowire.Type) error {
// reimplement protowire.ConsumeFieldValue without the extra case for groups (which
// are are complicted and not a thing in proto3).
var err error
switch fieldType {
case protowire.VarintType:
_, err = d.consumeVarint()
case protowire.Fixed32Type:
_, err = d.consumeFixed32()
case protowire.Fixed64Type:
_, err = d.consumeFixed64()
case protowire.BytesType:
_, err = d.consumeBytes()
default:
return fmt.Errorf("unknown field type %v in field %v", fieldType, fieldNum)
}
if err != nil {
return fmt.Errorf("consuming field %v of type %v: %w", fieldNum, fieldType, err)
}
return nil
}
// Consume a bytes field from the input. Returns offsets for the data in the buffer slices
// and an error.
func (d *readObjectResponseDecoder) consumeBytes() (bufferSliceOffsetsReadObject, error) {
// m is the length of the data past the tag.
m, err := d.consumeVarint()
if err != nil {
return bufferSliceOffsetsReadObject{}, fmt.Errorf("consuming bytes field: %w", err)
}
offsets := bufferSliceOffsetsReadObject{
startBuf: d.currBuf,
startOff: d.currOff,
currBuf: d.currBuf,
currOff: d.currOff,
}
// Advance offsets to lengths of bytes field and capture where we end.
d.advanceOffset(m)
offsets.endBuf = d.currBuf
offsets.endOff = d.currOff
return offsets, nil
}
// Consume a bytes field from the input and copy into a new buffer if
// necessary (if the data is split across buffers in databuf). This can be
// used to leverage proto.Unmarshal for small bytes fields (i.e. anything
// except object data).
func (d *readObjectResponseDecoder) consumeBytesCopy() ([]byte, error) {
// m is the length of the bytes data.
m, err := d.consumeVarint()
if err != nil {
return nil, fmt.Errorf("consuming varint: %w", err)
}
// Copy the data into a buffer and advance the offset
b := d.copyNextBytes(int(m))
if err := d.advanceOffset(m); err != nil {
return nil, fmt.Errorf("advancing offset: %w", err)
}
return b, nil
}
// readFullObjectResponse returns the ReadObjectResponse that is encoded in the
// wire-encoded message buffer b, or an error if the message is invalid.
// This must be used on the first recv of an object as it may contain all fields
// of ReadObjectResponse, and we use or pass on those fields to the user.
// This function is essentially identical to proto.Unmarshal, except it aliases
// the data in the input []byte. If the proto library adds a feature to
// Unmarshal that does that, this function can be dropped.
func (d *readObjectResponseDecoder) readFullObjectResponse() error {
msg := &storagepb.ReadObjectResponse{}
// Loop over the entire message, extracting fields as we go. This does not
// handle field concatenation, in which the contents of a single field
// are split across multiple protobuf tags.
for d.off < uint64(d.databufs.Len()) {
fieldNum, fieldType, err := d.consumeTag()
if err != nil {
return fmt.Errorf("consuming next tag: %w", err)
}
// Unmarshal the field according to its type. Only fields that are not
// nil will be present.
switch {
case fieldNum == checksummedDataFieldReadObject && fieldType == protowire.BytesType:
// The ChecksummedData field was found. Initialize the struct.
msg.ChecksummedData = &storagepb.ChecksummedData{}
bytesFieldLen, err := d.consumeVarint()
if err != nil {
return fmt.Errorf("consuming bytes: %v", err)
}
var contentEndOff = d.off + bytesFieldLen
for d.off < contentEndOff {
gotNum, gotTyp, err := d.consumeTag()
if err != nil {
return fmt.Errorf("consuming checksummedData tag: %w", err)
}
switch {
case gotNum == checksummedDataContentFieldReadObject && gotTyp == protowire.BytesType:
// Get the offsets of the content bytes.
d.dataOffsets, err = d.consumeBytes()
if err != nil {
return fmt.Errorf("invalid ReadObjectResponse.ChecksummedData.Content: %w", err)
}
case gotNum == checksummedDataCRC32CFieldReadObject && gotTyp == protowire.Fixed32Type:
v, err := d.consumeFixed32()
if err != nil {
return fmt.Errorf("invalid ReadObjectResponse.ChecksummedData.Crc32C: %w", err)
}
msg.ChecksummedData.Crc32C = &v
default:
err := d.consumeFieldValue(gotNum, gotTyp)
if err != nil {
return fmt.Errorf("invalid field in ReadObjectResponse.ChecksummedData: %w", err)
}
}
}
case fieldNum == objectChecksumsFieldReadObject && fieldType == protowire.BytesType:
// The field was found. Initialize the struct.
msg.ObjectChecksums = &storagepb.ObjectChecksums{}
// Consume the bytes and copy them into a single buffer if they are split across buffers.
buf, err := d.consumeBytesCopy()
if err != nil {
return fmt.Errorf("invalid ReadObjectResponse.ObjectChecksums: %v", err)
}
// Unmarshal.
if err := proto.Unmarshal(buf, msg.ObjectChecksums); err != nil {
return err
}
case fieldNum == contentRangeFieldReadObject && fieldType == protowire.BytesType:
msg.ContentRange = &storagepb.ContentRange{}
buf, err := d.consumeBytesCopy()
if err != nil {
return fmt.Errorf("invalid ReadObjectResponse.ContentRange: %v", err)
}
if err := proto.Unmarshal(buf, msg.ContentRange); err != nil {
return err
}
case fieldNum == metadataFieldReadObject && fieldType == protowire.BytesType:
msg.Metadata = &storagepb.Object{}
buf, err := d.consumeBytesCopy()
if err != nil {
return fmt.Errorf("invalid ReadObjectResponse.Metadata: %v", err)
}
if err := proto.Unmarshal(buf, msg.Metadata); err != nil {
return err
}
default:
err := d.consumeFieldValue(fieldNum, fieldType)
if err != nil {
return fmt.Errorf("invalid field in ReadObjectResponse: %w", err)
}
}
}
d.msg = msg
return nil
}
// reopenStream "closes" the existing stream and attempts to reopen a stream and
// sets the Reader's stream and cancelStream properties in the process.
func (r *gRPCReadObjectReader) reopenStream() error {
// Close existing stream and initialize new stream with updated offset.
r.Close()
res, cancel, err := r.reopen(r.seen)
if err != nil {
return err
}
r.stream = res.stream
r.currMsg = res.decoder
r.cancel = cancel
return nil
}

305
vendor/cloud.google.com/go/storage/grpc_writer.go generated vendored Normal file
View file

@ -0,0 +1,305 @@
// Copyright 2025 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 storage
import (
"context"
"errors"
"fmt"
"io"
gapic "cloud.google.com/go/storage/internal/apiv2"
"cloud.google.com/go/storage/internal/apiv2/storagepb"
gax "github.com/googleapis/gax-go/v2"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
type gRPCAppendBidiWriteBufferSender struct {
ctx context.Context
bucket string
routingToken *string
raw *gapic.Client
settings *settings
stream storagepb.Storage_BidiWriteObjectClient
firstMessage *storagepb.BidiWriteObjectRequest
objectChecksums *storagepb.ObjectChecksums
forceFirstMessage bool
flushOffset int64
// Fields used to report responses from the receive side of the stream
// recvs is closed when the current recv goroutine is complete. recvErr is set
// to the result of that stream (including io.EOF to indicate success)
recvs <-chan *storagepb.BidiWriteObjectResponse
recvErr error
}
func (w *gRPCWriter) newGRPCAppendBidiWriteBufferSender() (*gRPCAppendBidiWriteBufferSender, error) {
s := &gRPCAppendBidiWriteBufferSender{
ctx: w.ctx,
bucket: w.spec.GetResource().GetBucket(),
raw: w.c.raw,
settings: w.c.settings,
firstMessage: &storagepb.BidiWriteObjectRequest{
FirstMessage: &storagepb.BidiWriteObjectRequest_WriteObjectSpec{
WriteObjectSpec: w.spec,
},
CommonObjectRequestParams: toProtoCommonObjectRequestParams(w.encryptionKey),
},
objectChecksums: toProtoChecksums(w.sendCRC32C, w.attrs),
forceFirstMessage: true,
}
return s, nil
}
func (s *gRPCAppendBidiWriteBufferSender) connect() (err error) {
err = func() error {
// If this is a forced first message, we've already determined it's safe to
// send.
if s.forceFirstMessage {
s.forceFirstMessage = false
return nil
}
// It's always ok to reconnect if there is a handle. This is the common
// case.
if s.firstMessage.GetAppendObjectSpec().GetWriteHandle() != nil {
return nil
}
// We can also reconnect if the first message has an if_generation_match or
// if_metageneration_match condition. Note that negative conditions like
// if_generation_not_match are not necessarily safe to retry.
aos := s.firstMessage.GetAppendObjectSpec()
wos := s.firstMessage.GetWriteObjectSpec()
if aos != nil && aos.IfMetagenerationMatch != nil {
return nil
}
if wos != nil && wos.IfGenerationMatch != nil {
return nil
}
if wos != nil && wos.IfMetagenerationMatch != nil {
return nil
}
// Otherwise, it is not safe to reconnect.
return errors.New("cannot safely reconnect; no write handle or preconditions")
}()
if err != nil {
return err
}
return s.startReceiver()
}
func (s *gRPCAppendBidiWriteBufferSender) withRequestParams(ctx context.Context) context.Context {
param := fmt.Sprintf("appendable=true&bucket=%s", s.bucket)
if s.routingToken != nil {
param = param + fmt.Sprintf("&routing_token=%s", *s.routingToken)
}
return gax.InsertMetadataIntoOutgoingContext(s.ctx, "x-goog-request-params", param)
}
func (s *gRPCAppendBidiWriteBufferSender) startReceiver() (err error) {
s.stream, err = s.raw.BidiWriteObject(s.withRequestParams(s.ctx), s.settings.gax...)
if err != nil {
return
}
recvs := make(chan *storagepb.BidiWriteObjectResponse)
s.recvs = recvs
s.recvErr = nil
go s.receiveMessages(recvs)
return
}
func (s *gRPCAppendBidiWriteBufferSender) ensureFirstMessageAppendObjectSpec() {
if s.firstMessage.GetWriteObjectSpec() != nil {
w := s.firstMessage.GetWriteObjectSpec()
s.firstMessage.FirstMessage = &storagepb.BidiWriteObjectRequest_AppendObjectSpec{
AppendObjectSpec: &storagepb.AppendObjectSpec{
Bucket: w.GetResource().GetBucket(),
Object: w.GetResource().GetName(),
IfMetagenerationMatch: w.IfMetagenerationMatch,
IfMetagenerationNotMatch: w.IfMetagenerationNotMatch,
},
}
}
}
func (s *gRPCAppendBidiWriteBufferSender) maybeUpdateFirstMessage(resp *storagepb.BidiWriteObjectResponse) {
// Any affirmative response should switch us to an AppendObjectSpec.
s.ensureFirstMessageAppendObjectSpec()
if r := resp.GetResource(); r != nil {
aos := s.firstMessage.GetAppendObjectSpec()
aos.Bucket = r.GetBucket()
aos.Object = r.GetName()
aos.Generation = r.GetGeneration()
}
if h := resp.GetWriteHandle(); h != nil {
s.firstMessage.GetAppendObjectSpec().WriteHandle = h
}
}
type bidiWriteObjectRedirectionError struct{}
func (e bidiWriteObjectRedirectionError) Error() string {
return "BidiWriteObjectRedirectedError"
}
func (s *gRPCAppendBidiWriteBufferSender) handleRedirectionError(e *storagepb.BidiWriteObjectRedirectedError) bool {
if e.RoutingToken == nil {
// This shouldn't happen, but we don't want to blindly retry here. Instead,
// surface the error to the caller.
return false
}
if e.WriteHandle != nil {
// If we get back a write handle, we should use it. We can only use it
// on an append object spec.
s.ensureFirstMessageAppendObjectSpec()
s.firstMessage.GetAppendObjectSpec().WriteHandle = e.WriteHandle
// Generation is meant to only come with the WriteHandle, so ignore it
// otherwise.
if e.Generation != nil {
s.firstMessage.GetAppendObjectSpec().Generation = e.GetGeneration()
}
}
s.routingToken = e.RoutingToken
return true
}
func (s *gRPCAppendBidiWriteBufferSender) receiveMessages(resps chan<- *storagepb.BidiWriteObjectResponse) {
resp, err := s.stream.Recv()
for err == nil {
s.maybeUpdateFirstMessage(resp)
if resp.WriteStatus != nil {
// We only get a WriteStatus if this was a solicited message (either
// state_lookup: true or finish_write: true). Unsolicited messages may
// arrive to update our handle if necessary. We don't want to block on
// this channel write if this was an unsolicited message.
resps <- resp
}
resp, err = s.stream.Recv()
}
if st, ok := status.FromError(err); ok && st.Code() == codes.Aborted {
for _, d := range st.Details() {
if e, ok := d.(*storagepb.BidiWriteObjectRedirectedError); ok {
// If we can handle this error, replace it with the sentinel. Otherwise,
// report it to the user.
if ok := s.handleRedirectionError(e); ok {
err = bidiWriteObjectRedirectionError{}
}
}
}
}
// TODO: automatically reconnect on retriable recv errors, even if there are
// no sends occurring.
s.recvErr = err
close(resps)
}
func (s *gRPCAppendBidiWriteBufferSender) sendOnConnectedStream(buf []byte, offset int64, flush, finishWrite, sendFirstMessage bool) (obj *storagepb.Object, err error) {
req := bidiWriteObjectRequest(buf, offset, flush, finishWrite)
if finishWrite {
// appendable objects pass checksums on the last message only
req.ObjectChecksums = s.objectChecksums
}
if sendFirstMessage {
proto.Merge(req, s.firstMessage)
}
if err = s.stream.Send(req); err != nil {
return nil, err
}
if finishWrite {
s.stream.CloseSend()
for resp := range s.recvs {
if resp.GetResource() != nil {
obj = resp.GetResource()
}
}
if s.recvErr != io.EOF {
return nil, s.recvErr
}
return
}
if flush {
// We don't necessarily expect multiple responses for a single flush, but
// this allows the server to send multiple responses if it wants to.
for s.flushOffset < offset+int64(len(buf)) {
resp, ok := <-s.recvs
if !ok {
return nil, s.recvErr
}
pSize := resp.GetPersistedSize()
rSize := resp.GetResource().GetSize()
if s.flushOffset < pSize {
s.flushOffset = pSize
}
if s.flushOffset < rSize {
s.flushOffset = rSize
}
}
}
return
}
func (s *gRPCAppendBidiWriteBufferSender) sendBuffer(buf []byte, offset int64, flush, finishWrite bool) (obj *storagepb.Object, err error) {
for {
sendFirstMessage := false
if s.stream == nil {
sendFirstMessage = true
if err = s.connect(); err != nil {
return
}
}
obj, err = s.sendOnConnectedStream(buf, offset, flush, finishWrite, sendFirstMessage)
if err == nil {
return
}
// await recv stream termination
for range s.recvs {
}
if s.recvErr != io.EOF {
err = s.recvErr
}
s.stream = nil
// Retry transparently on a redirection error
if _, ok := err.(bidiWriteObjectRedirectionError); ok {
s.forceFirstMessage = true
continue
}
return
}
}

View file

@ -22,6 +22,7 @@ import (
"hash/crc32"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
@ -47,13 +48,14 @@ import (
// httpStorageClient is the HTTP-JSON API implementation of the transport-agnostic
// storageClient interface.
type httpStorageClient struct {
creds *google.Credentials
hc *http.Client
xmlHost string
raw *raw.Service
scheme string
settings *settings
config *storageConfig
creds *google.Credentials
hc *http.Client
xmlHost string
raw *raw.Service
scheme string
settings *settings
config *storageConfig
dynamicReadReqStallTimeout *bucketDelayManager
}
// newHTTPStorageClient initializes a new storageClient that uses the HTTP-JSON
@ -128,14 +130,29 @@ func newHTTPStorageClient(ctx context.Context, opts ...storageOption) (storageCl
return nil, fmt.Errorf("supplied endpoint %q is not valid: %w", ep, err)
}
var bd *bucketDelayManager
if config.readStallTimeoutConfig != nil {
drrstConfig := config.readStallTimeoutConfig
bd, err = newBucketDelayManager(
drrstConfig.TargetPercentile,
getDynamicReadReqIncreaseRateFromEnv(),
getDynamicReadReqInitialTimeoutSecFromEnv(drrstConfig.Min),
drrstConfig.Min,
defaultDynamicReqdReqMaxTimeout)
if err != nil {
return nil, fmt.Errorf("creating dynamic-delay: %w", err)
}
}
return &httpStorageClient{
creds: creds,
hc: hc,
xmlHost: u.Host,
raw: rawService,
scheme: u.Scheme,
settings: s,
config: &config,
creds: creds,
hc: hc,
xmlHost: u.Host,
raw: rawService,
scheme: u.Scheme,
settings: s,
config: &config,
dynamicReadReqStallTimeout: bd,
}, nil
}
@ -575,6 +592,31 @@ func (c *httpStorageClient) RestoreObject(ctx context.Context, params *restoreOb
return newObject(obj), err
}
func (c *httpStorageClient) MoveObject(ctx context.Context, params *moveObjectParams, opts ...storageOption) (*ObjectAttrs, error) {
s := callSettings(c.settings, opts...)
req := c.raw.Objects.Move(params.bucket, params.srcObject, params.dstObject).Context(ctx)
if err := applyConds("MoveObjectDestination", defaultGen, params.dstConds, req); err != nil {
return nil, err
}
if err := applySourceConds("MoveObjectSource", defaultGen, params.srcConds, req); err != nil {
return nil, err
}
if s.userProject != "" {
req.UserProject(s.userProject)
}
if err := setEncryptionHeaders(req.Header(), params.encryptionKey, false); err != nil {
return nil, err
}
var obj *raw.Object
var err error
err = run(ctx, func(ctx context.Context) error { obj, err = req.Context(ctx).Do(); return err }, s.retry, s.idempotent)
var e *googleapi.Error
if ok := errors.As(err, &e); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
}
return newObject(obj), err
}
// Default Object ACL methods.
func (c *httpStorageClient) DeleteDefaultObjectACL(ctx context.Context, bucket string, entity ACLEntity, opts ...storageOption) error {
@ -780,7 +822,7 @@ func (c *httpStorageClient) RewriteObject(ctx context.Context, req *rewriteObjec
if err := applyConds("Copy destination", defaultGen, req.dstObject.conds, call); err != nil {
return nil, err
}
if err := applySourceConds(req.srcObject.gen, req.srcObject.conds, call); err != nil {
if err := applySourceConds("Copy source", req.srcObject.gen, req.srcObject.conds, call); err != nil {
return nil, err
}
if s.userProject != "" {
@ -819,6 +861,11 @@ func (c *httpStorageClient) RewriteObject(ctx context.Context, req *rewriteObjec
return r, nil
}
// NewMultiRangeDownloader is not supported by http client.
func (c *httpStorageClient) NewMultiRangeDownloader(ctx context.Context, params *newMultiRangeDownloaderParams, opts ...storageOption) (mr *MultiRangeDownloader, err error) {
return nil, errMethodNotSupported
}
func (c *httpStorageClient) NewRangeReader(ctx context.Context, params *newRangeReaderParams, opts ...storageOption) (r *Reader, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.httpStorageClient.NewRangeReader")
defer func() { trace.EndSpan(ctx, err) }()
@ -858,7 +905,47 @@ func (c *httpStorageClient) newRangeReaderXML(ctx context.Context, params *newRa
reopen := readerReopen(ctx, req.Header, params, s,
func(ctx context.Context) (*http.Response, error) {
setHeadersFromCtx(ctx, req.Header)
return c.hc.Do(req.WithContext(ctx))
if c.dynamicReadReqStallTimeout == nil {
return c.hc.Do(req.WithContext(ctx))
}
cancelCtx, cancel := context.WithCancel(ctx)
var (
res *http.Response
err error
)
done := make(chan bool)
go func() {
reqStartTime := time.Now()
res, err = c.hc.Do(req.WithContext(cancelCtx))
if err == nil {
reqLatency := time.Since(reqStartTime)
c.dynamicReadReqStallTimeout.update(params.bucket, reqLatency)
} else if errors.Is(err, context.Canceled) {
// context.Canceled means operation took more than current dynamicTimeout,
// hence should be increased.
c.dynamicReadReqStallTimeout.increase(params.bucket)
}
done <- true
}()
// Wait until stall timeout or request is successful.
stallTimeout := c.dynamicReadReqStallTimeout.getValue(params.bucket)
timer := time.After(stallTimeout)
select {
case <-timer:
log.Printf("stalled read-req (%p) cancelled after %fs", req, stallTimeout.Seconds())
cancel()
err = context.DeadlineExceeded
if res != nil && res.Body != nil {
res.Body.Close()
}
case <-done:
cancel = nil
}
return res, err
},
func() error { return setConditionsHeaders(req.Header, params.conds) },
func() { req.URL.RawQuery = fmt.Sprintf("generation=%d", params.gen) })
@ -895,6 +982,10 @@ func (c *httpStorageClient) newRangeReaderJSON(ctx context.Context, params *newR
}
func (c *httpStorageClient) OpenWriter(params *openWriterParams, opts ...storageOption) (*io.PipeWriter, error) {
if params.append {
return nil, errors.New("storage: append not supported on HTTP Client; use gRPC")
}
s := callSettings(c.settings, opts...)
errorf := params.setError
setObj := params.setObj
@ -910,6 +1001,9 @@ func (c *httpStorageClient) OpenWriter(params *openWriterParams, opts ...storage
if params.chunkRetryDeadline != 0 {
mediaOpts = append(mediaOpts, googleapi.ChunkRetryDeadline(params.chunkRetryDeadline))
}
if params.chunkTransferTimeout != 0 {
mediaOpts = append(mediaOpts, googleapi.ChunkTransferTimeout(params.chunkTransferTimeout))
}
pr, pw := io.Pipe()
@ -1456,6 +1550,14 @@ func parseReadResponse(res *http.Response, params *newRangeReaderParams, reopen
}
}
metadata := map[string]string{}
for key, values := range res.Header {
if len(values) > 0 && strings.HasPrefix(key, "X-Goog-Meta-") {
key := key[len("X-Goog-Meta-"):]
metadata[key] = values[0]
}
}
attrs := ReaderObjectAttrs{
Size: size,
ContentType: res.Header.Get("Content-Type"),
@ -1469,10 +1571,11 @@ func parseReadResponse(res *http.Response, params *newRangeReaderParams, reopen
Decompressed: res.Uncompressed || uncompressedByServer(res),
}
return &Reader{
Attrs: attrs,
size: size,
remain: remain,
checkCRC: checkCRC,
Attrs: attrs,
objectMetadata: &metadata,
size: size,
remain: remain,
checkCRC: checkCRC,
reader: &httpReader{
reopen: reopen,
body: body,

View file

@ -1,4 +1,4 @@
// Copyright 2024 Google LLC
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -41,7 +41,7 @@ type BucketIterator struct {
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Bucket, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
// PageInfo supports pagination. See the [google.golang.org/api/iterator] package for details.
func (it *BucketIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
@ -68,100 +68,6 @@ func (it *BucketIterator) takeBuf() interface{} {
return b
}
// HmacKeyMetadataIterator manages a stream of *storagepb.HmacKeyMetadata.
type HmacKeyMetadataIterator struct {
items []*storagepb.HmacKeyMetadata
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.HmacKeyMetadata, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *HmacKeyMetadataIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *HmacKeyMetadataIterator) Next() (*storagepb.HmacKeyMetadata, error) {
var item *storagepb.HmacKeyMetadata
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *HmacKeyMetadataIterator) bufLen() int {
return len(it.items)
}
func (it *HmacKeyMetadataIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// NotificationConfigIterator manages a stream of *storagepb.NotificationConfig.
type NotificationConfigIterator struct {
items []*storagepb.NotificationConfig
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.NotificationConfig, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *NotificationConfigIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *NotificationConfigIterator) Next() (*storagepb.NotificationConfig, error) {
var item *storagepb.NotificationConfig
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *NotificationConfigIterator) bufLen() int {
return len(it.items)
}
func (it *NotificationConfigIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// ObjectIterator manages a stream of *storagepb.Object.
type ObjectIterator struct {
items []*storagepb.Object
@ -182,7 +88,7 @@ type ObjectIterator struct {
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Object, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
// PageInfo supports pagination. See the [google.golang.org/api/iterator] package for details.
func (it *ObjectIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}

View file

@ -1,4 +1,4 @@
// Copyright 2024 Google LLC
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -31,18 +31,6 @@ func (it *BucketIterator) All() iter.Seq2[*storagepb.Bucket, error] {
return iterator.RangeAdapter(it.Next)
}
// All returns an iterator. If an error is returned by the iterator, the
// iterator will stop after that iteration.
func (it *HmacKeyMetadataIterator) All() iter.Seq2[*storagepb.HmacKeyMetadata, error] {
return iterator.RangeAdapter(it.Next)
}
// All returns an iterator. If an error is returned by the iterator, the
// iterator will stop after that iteration.
func (it *NotificationConfigIterator) All() iter.Seq2[*storagepb.NotificationConfig, error] {
return iterator.RangeAdapter(it.Next)
}
// All returns an iterator. If an error is returned by the iterator, the
// iterator will stop after that iteration.
func (it *ObjectIterator) All() iter.Seq2[*storagepb.Object, error] {

View file

@ -1,4 +1,4 @@
// Copyright 2024 Google LLC
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -43,6 +43,7 @@
//
// To get started with this package, create a client.
//
// // go get cloud.google.com/go/storage/internal/apiv2@latest
// ctx := context.Background()
// // This snippet has been automatically generated and should be regarded as a code template only.
// // It will require modifications to work:
@ -61,25 +62,14 @@
//
// # Using the Client
//
// The following is an example of making an API call with the newly created client.
// The following is an example of making an API call with the newly created client, mentioned above.
//
// ctx := context.Background()
// // This snippet has been automatically generated and should be regarded as a code template only.
// // It will require modifications to work:
// // - It may require correct/in-range values for request initialization.
// // - It may require specifying regional endpoints when creating the service client as shown in:
// // https://pkg.go.dev/cloud.google.com/go#hdr-Client_Options
// c, err := storage.NewClient(ctx)
// if err != nil {
// // TODO: Handle error.
// }
// defer c.Close()
// stream, err := c.BidiWriteObject(ctx)
// stream, err := c.BidiReadObject(ctx)
// if err != nil {
// // TODO: Handle error.
// }
// go func() {
// reqs := []*storagepb.BidiWriteObjectRequest{
// reqs := []*storagepb.BidiReadObjectRequest{
// // TODO: Create requests.
// }
// for _, req := range reqs {
@ -115,34 +105,3 @@
// [Debugging Client Libraries]: https://pkg.go.dev/cloud.google.com/go#hdr-Debugging
// [Inspecting errors]: https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors
package storage // import "cloud.google.com/go/storage/internal/apiv2"
import (
"context"
"google.golang.org/api/option"
)
// For more information on implementing a client constructor hook, see
// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
type clientHookParams struct{}
type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
var versionClient string
func getVersionClient() string {
if versionClient == "" {
return "UNKNOWN"
}
return versionClient
}
// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
func DefaultAuthScopes() []string {
return []string{
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/cloud-platform.read-only",
"https://www.googleapis.com/auth/devstorage.full_control",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/devstorage.read_write",
}
}

View file

@ -10,6 +10,11 @@
"grpc": {
"libraryClient": "Client",
"rpcs": {
"BidiReadObject": {
"methods": [
"BidiReadObject"
]
},
"BidiWriteObject": {
"methods": [
"BidiWriteObject"
@ -30,31 +35,11 @@
"CreateBucket"
]
},
"CreateHmacKey": {
"methods": [
"CreateHmacKey"
]
},
"CreateNotificationConfig": {
"methods": [
"CreateNotificationConfig"
]
},
"DeleteBucket": {
"methods": [
"DeleteBucket"
]
},
"DeleteHmacKey": {
"methods": [
"DeleteHmacKey"
]
},
"DeleteNotificationConfig": {
"methods": [
"DeleteNotificationConfig"
]
},
"DeleteObject": {
"methods": [
"DeleteObject"
@ -65,46 +50,21 @@
"GetBucket"
]
},
"GetHmacKey": {
"methods": [
"GetHmacKey"
]
},
"GetIamPolicy": {
"methods": [
"GetIamPolicy"
]
},
"GetNotificationConfig": {
"methods": [
"GetNotificationConfig"
]
},
"GetObject": {
"methods": [
"GetObject"
]
},
"GetServiceAccount": {
"methods": [
"GetServiceAccount"
]
},
"ListBuckets": {
"methods": [
"ListBuckets"
]
},
"ListHmacKeys": {
"methods": [
"ListHmacKeys"
]
},
"ListNotificationConfigs": {
"methods": [
"ListNotificationConfigs"
]
},
"ListObjects": {
"methods": [
"ListObjects"
@ -115,6 +75,11 @@
"LockBucketRetentionPolicy"
]
},
"MoveObject": {
"methods": [
"MoveObject"
]
},
"QueryWriteStatus": {
"methods": [
"QueryWriteStatus"
@ -155,11 +120,6 @@
"UpdateBucket"
]
},
"UpdateHmacKey": {
"methods": [
"UpdateHmacKey"
]
},
"UpdateObject": {
"methods": [
"UpdateObject"

View file

@ -0,0 +1,65 @@
// Copyright 2025 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
//
// https://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.
// Code generated by protoc-gen-go_gapic. DO NOT EDIT.
package storage
import (
"context"
"log/slog"
"github.com/googleapis/gax-go/v2/internallog/grpclog"
"google.golang.org/api/option"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
)
const serviceName = "storage.googleapis.com"
// For more information on implementing a client constructor hook, see
// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.
type clientHookParams struct{}
type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error)
var versionClient string
func getVersionClient() string {
if versionClient == "" {
return "UNKNOWN"
}
return versionClient
}
// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
func DefaultAuthScopes() []string {
return []string{
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/cloud-platform.read-only",
"https://www.googleapis.com/auth/devstorage.full_control",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/devstorage.read_write",
}
}
func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) {
var zero O
logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req))
resp, err := fn(ctx, req, opts...)
if err != nil {
return zero, err
}
logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp))
return resp, err
}

View file

@ -1,4 +1,4 @@
// Copyright 2024 Google LLC
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@ package storage
import (
"context"
"fmt"
"log/slog"
"math"
"net/url"
"regexp"
@ -56,6 +57,7 @@ type CallOptions struct {
CancelResumableWrite []gax.CallOption
GetObject []gax.CallOption
ReadObject []gax.CallOption
BidiReadObject []gax.CallOption
UpdateObject []gax.CallOption
WriteObject []gax.CallOption
BidiWriteObject []gax.CallOption
@ -63,16 +65,7 @@ type CallOptions struct {
RewriteObject []gax.CallOption
StartResumableWrite []gax.CallOption
QueryWriteStatus []gax.CallOption
GetServiceAccount []gax.CallOption
CreateHmacKey []gax.CallOption
DeleteHmacKey []gax.CallOption
GetHmacKey []gax.CallOption
ListHmacKeys []gax.CallOption
UpdateHmacKey []gax.CallOption
DeleteNotificationConfig []gax.CallOption
GetNotificationConfig []gax.CallOption
CreateNotificationConfig []gax.CallOption
ListNotificationConfigs []gax.CallOption
MoveObject []gax.CallOption
}
func defaultGRPCClientOptions() []option.ClientOption {
@ -286,6 +279,18 @@ func defaultCallOptions() *CallOptions {
})
}),
},
BidiReadObject: []gax.CallOption{
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
UpdateObject: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
@ -375,124 +380,7 @@ func defaultCallOptions() *CallOptions {
})
}),
},
GetServiceAccount: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
CreateHmacKey: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
DeleteHmacKey: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
GetHmacKey: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
ListHmacKeys: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
UpdateHmacKey: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
DeleteNotificationConfig: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
GetNotificationConfig: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
CreateNotificationConfig: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 1000 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 2.00,
})
}),
},
ListNotificationConfigs: []gax.CallOption{
MoveObject: []gax.CallOption{
gax.WithTimeout(60000 * time.Millisecond),
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
@ -528,6 +416,7 @@ type internalClient interface {
CancelResumableWrite(context.Context, *storagepb.CancelResumableWriteRequest, ...gax.CallOption) (*storagepb.CancelResumableWriteResponse, error)
GetObject(context.Context, *storagepb.GetObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
ReadObject(context.Context, *storagepb.ReadObjectRequest, ...gax.CallOption) (storagepb.Storage_ReadObjectClient, error)
BidiReadObject(context.Context, ...gax.CallOption) (storagepb.Storage_BidiReadObjectClient, error)
UpdateObject(context.Context, *storagepb.UpdateObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
WriteObject(context.Context, ...gax.CallOption) (storagepb.Storage_WriteObjectClient, error)
BidiWriteObject(context.Context, ...gax.CallOption) (storagepb.Storage_BidiWriteObjectClient, error)
@ -535,16 +424,7 @@ type internalClient interface {
RewriteObject(context.Context, *storagepb.RewriteObjectRequest, ...gax.CallOption) (*storagepb.RewriteResponse, error)
StartResumableWrite(context.Context, *storagepb.StartResumableWriteRequest, ...gax.CallOption) (*storagepb.StartResumableWriteResponse, error)
QueryWriteStatus(context.Context, *storagepb.QueryWriteStatusRequest, ...gax.CallOption) (*storagepb.QueryWriteStatusResponse, error)
GetServiceAccount(context.Context, *storagepb.GetServiceAccountRequest, ...gax.CallOption) (*storagepb.ServiceAccount, error)
CreateHmacKey(context.Context, *storagepb.CreateHmacKeyRequest, ...gax.CallOption) (*storagepb.CreateHmacKeyResponse, error)
DeleteHmacKey(context.Context, *storagepb.DeleteHmacKeyRequest, ...gax.CallOption) error
GetHmacKey(context.Context, *storagepb.GetHmacKeyRequest, ...gax.CallOption) (*storagepb.HmacKeyMetadata, error)
ListHmacKeys(context.Context, *storagepb.ListHmacKeysRequest, ...gax.CallOption) *HmacKeyMetadataIterator
UpdateHmacKey(context.Context, *storagepb.UpdateHmacKeyRequest, ...gax.CallOption) (*storagepb.HmacKeyMetadata, error)
DeleteNotificationConfig(context.Context, *storagepb.DeleteNotificationConfigRequest, ...gax.CallOption) error
GetNotificationConfig(context.Context, *storagepb.GetNotificationConfigRequest, ...gax.CallOption) (*storagepb.NotificationConfig, error)
CreateNotificationConfig(context.Context, *storagepb.CreateNotificationConfigRequest, ...gax.CallOption) (*storagepb.NotificationConfig, error)
ListNotificationConfigs(context.Context, *storagepb.ListNotificationConfigsRequest, ...gax.CallOption) *NotificationConfigIterator
MoveObject(context.Context, *storagepb.MoveObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
}
// Client is a client for interacting with Cloud Storage API.
@ -664,12 +544,26 @@ func (c *Client) ComposeObject(ctx context.Context, req *storagepb.ComposeObject
return c.internalClient.ComposeObject(ctx, req, opts...)
}
// DeleteObject deletes an object and its metadata.
// DeleteObject deletes an object and its metadata. Deletions are permanent if versioning
// is not enabled for the bucket, or if the generation parameter is used, or
// if soft delete (at https://cloud.google.com/storage/docs/soft-delete) is not
// enabled for the bucket.
// When this API is used to delete an object from a bucket that has soft
// delete policy enabled, the object becomes soft deleted, and the
// softDeleteTime and hardDeleteTime properties are set on the object.
// This API cannot be used to permanently delete soft-deleted objects.
// Soft-deleted objects are permanently deleted according to their
// hardDeleteTime.
//
// Deletions are normally permanent when versioning is disabled or whenever
// the generation parameter is used. However, if soft delete is enabled for
// the bucket, deleted objects can be restored using RestoreObject until the
// soft delete retention period has passed.
// You can use the [RestoreObject][google.storage.v2.Storage.RestoreObject]
// API to restore soft-deleted objects until the soft delete retention period
// has passed.
//
// IAM Permissions:
//
// Requires storage.objects.delete
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) DeleteObject(ctx context.Context, req *storagepb.DeleteObjectRequest, opts ...gax.CallOption) error {
return c.internalClient.DeleteObject(ctx, req, opts...)
}
@ -691,16 +585,52 @@ func (c *Client) CancelResumableWrite(ctx context.Context, req *storagepb.Cancel
return c.internalClient.CancelResumableWrite(ctx, req, opts...)
}
// GetObject retrieves an objects metadata.
// GetObject retrieves object metadata.
//
// IAM Permissions:
//
// Requires storage.objects.get
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket. To return object ACLs, the authenticated user must also have
// the storage.objects.getIamPolicy permission.
func (c *Client) GetObject(ctx context.Context, req *storagepb.GetObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
return c.internalClient.GetObject(ctx, req, opts...)
}
// ReadObject reads an objects data.
// ReadObject retrieves object data.
//
// IAM Permissions:
//
// Requires storage.objects.get
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) ReadObject(ctx context.Context, req *storagepb.ReadObjectRequest, opts ...gax.CallOption) (storagepb.Storage_ReadObjectClient, error) {
return c.internalClient.ReadObject(ctx, req, opts...)
}
// BidiReadObject reads an objects data.
//
// This is a bi-directional API with the added support for reading multiple
// ranges within one stream both within and across multiple messages.
// If the server encountered an error for any of the inputs, the stream will
// be closed with the relevant error code.
// Because the API allows for multiple outstanding requests, when the stream
// is closed the error response will contain a BidiReadObjectRangesError proto
// in the error extension describing the error for each outstanding read_id.
//
// IAM Permissions:
//
// # Requires storage.objects.get
//
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
//
// This API is currently in preview and is not yet available for general
// use.
func (c *Client) BidiReadObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_BidiReadObjectClient, error) {
return c.internalClient.BidiReadObject(ctx, opts...)
}
// UpdateObject updates an objects metadata.
// Equivalent to JSON APIs storage.objects.patch.
func (c *Client) UpdateObject(ctx context.Context, req *storagepb.UpdateObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
@ -770,6 +700,12 @@ func (c *Client) UpdateObject(ctx context.Context, req *storagepb.UpdateObjectRe
// Alternatively, the BidiWriteObject operation may be used to write an
// object with controls over flushing and the ability to fetch the ability to
// determine the current persisted size.
//
// IAM Permissions:
//
// Requires storage.objects.create
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) WriteObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_WriteObjectClient, error) {
return c.internalClient.WriteObject(ctx, opts...)
}
@ -794,6 +730,13 @@ func (c *Client) BidiWriteObject(ctx context.Context, opts ...gax.CallOption) (s
}
// ListObjects retrieves a list of objects matching the criteria.
//
// IAM Permissions:
//
// The authenticated user requires storage.objects.list
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions)
// to use this method. To return object ACLs, the authenticated user must also
// have the storage.objects.getIamPolicy permission.
func (c *Client) ListObjects(ctx context.Context, req *storagepb.ListObjectsRequest, opts ...gax.CallOption) *ObjectIterator {
return c.internalClient.ListObjects(ctx, req, opts...)
}
@ -804,101 +747,47 @@ func (c *Client) RewriteObject(ctx context.Context, req *storagepb.RewriteObject
return c.internalClient.RewriteObject(ctx, req, opts...)
}
// StartResumableWrite starts a resumable write. How long the write operation remains valid, and
// what happens when the write operation becomes invalid, are
// service-dependent.
// StartResumableWrite starts a resumable write operation. This
// method is part of the Resumable
// upload (at https://cloud.google.com/storage/docs/resumable-uploads) feature.
// This allows you to upload large objects in multiple chunks, which is more
// resilient to network interruptions than a single upload. The validity
// duration of the write operation, and the consequences of it becoming
// invalid, are service-dependent.
//
// IAM Permissions:
//
// Requires storage.objects.create
// IAM permission (at https://cloud.google.com/iam/docs/overview#permissions) on
// the bucket.
func (c *Client) StartResumableWrite(ctx context.Context, req *storagepb.StartResumableWriteRequest, opts ...gax.CallOption) (*storagepb.StartResumableWriteResponse, error) {
return c.internalClient.StartResumableWrite(ctx, req, opts...)
}
// QueryWriteStatus determines the persisted_size for an object that is being written, which
// can then be used as the write_offset for the next Write() call.
// QueryWriteStatus determines the persisted_size of an object that is being written. This
// method is part of the resumable
// upload (at https://cloud.google.com/storage/docs/resumable-uploads) feature.
// The returned value is the size of the object that has been persisted so
// far. The value can be used as the write_offset for the next Write()
// call.
//
// If the object does not exist (i.e., the object has been deleted, or the
// first Write() has not yet reached the service), this method returns the
// If the object does not exist, meaning if it was deleted, or the
// first Write() has not yet reached the service, this method returns the
// error NOT_FOUND.
//
// The client may call QueryWriteStatus() at any time to determine how
// much data has been processed for this object. This is useful if the
// client is buffering data and needs to know which data can be safely
// evicted. For any sequence of QueryWriteStatus() calls for a given
// object name, the sequence of returned persisted_size values will be
// This method is useful for clients that buffer data and need to know which
// data can be safely evicted. The client can call QueryWriteStatus() at any
// time to determine how much data has been logged for this object.
// For any sequence of QueryWriteStatus() calls for a given
// object name, the sequence of returned persisted_size values are
// non-decreasing.
func (c *Client) QueryWriteStatus(ctx context.Context, req *storagepb.QueryWriteStatusRequest, opts ...gax.CallOption) (*storagepb.QueryWriteStatusResponse, error) {
return c.internalClient.QueryWriteStatus(ctx, req, opts...)
}
// GetServiceAccount retrieves the name of a projects Google Cloud Storage service account.
//
// Deprecated: GetServiceAccount may be removed in a future version.
func (c *Client) GetServiceAccount(ctx context.Context, req *storagepb.GetServiceAccountRequest, opts ...gax.CallOption) (*storagepb.ServiceAccount, error) {
return c.internalClient.GetServiceAccount(ctx, req, opts...)
}
// CreateHmacKey creates a new HMAC key for the given service account.
//
// Deprecated: CreateHmacKey may be removed in a future version.
func (c *Client) CreateHmacKey(ctx context.Context, req *storagepb.CreateHmacKeyRequest, opts ...gax.CallOption) (*storagepb.CreateHmacKeyResponse, error) {
return c.internalClient.CreateHmacKey(ctx, req, opts...)
}
// DeleteHmacKey deletes a given HMAC key. Key must be in an INACTIVE state.
//
// Deprecated: DeleteHmacKey may be removed in a future version.
func (c *Client) DeleteHmacKey(ctx context.Context, req *storagepb.DeleteHmacKeyRequest, opts ...gax.CallOption) error {
return c.internalClient.DeleteHmacKey(ctx, req, opts...)
}
// GetHmacKey gets an existing HMAC key metadata for the given id.
//
// Deprecated: GetHmacKey may be removed in a future version.
func (c *Client) GetHmacKey(ctx context.Context, req *storagepb.GetHmacKeyRequest, opts ...gax.CallOption) (*storagepb.HmacKeyMetadata, error) {
return c.internalClient.GetHmacKey(ctx, req, opts...)
}
// ListHmacKeys lists HMAC keys under a given project with the additional filters provided.
//
// Deprecated: ListHmacKeys may be removed in a future version.
func (c *Client) ListHmacKeys(ctx context.Context, req *storagepb.ListHmacKeysRequest, opts ...gax.CallOption) *HmacKeyMetadataIterator {
return c.internalClient.ListHmacKeys(ctx, req, opts...)
}
// UpdateHmacKey updates a given HMAC key state between ACTIVE and INACTIVE.
//
// Deprecated: UpdateHmacKey may be removed in a future version.
func (c *Client) UpdateHmacKey(ctx context.Context, req *storagepb.UpdateHmacKeyRequest, opts ...gax.CallOption) (*storagepb.HmacKeyMetadata, error) {
return c.internalClient.UpdateHmacKey(ctx, req, opts...)
}
// DeleteNotificationConfig permanently deletes a NotificationConfig.
//
// Deprecated: DeleteNotificationConfig may be removed in a future version.
func (c *Client) DeleteNotificationConfig(ctx context.Context, req *storagepb.DeleteNotificationConfigRequest, opts ...gax.CallOption) error {
return c.internalClient.DeleteNotificationConfig(ctx, req, opts...)
}
// GetNotificationConfig view a NotificationConfig.
//
// Deprecated: GetNotificationConfig may be removed in a future version.
func (c *Client) GetNotificationConfig(ctx context.Context, req *storagepb.GetNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
return c.internalClient.GetNotificationConfig(ctx, req, opts...)
}
// CreateNotificationConfig creates a NotificationConfig for a given bucket.
// These NotificationConfigs, when triggered, publish messages to the
// specified Pub/Sub topics. See
// https://cloud.google.com/storage/docs/pubsub-notifications (at https://cloud.google.com/storage/docs/pubsub-notifications).
//
// Deprecated: CreateNotificationConfig may be removed in a future version.
func (c *Client) CreateNotificationConfig(ctx context.Context, req *storagepb.CreateNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
return c.internalClient.CreateNotificationConfig(ctx, req, opts...)
}
// ListNotificationConfigs retrieves a list of NotificationConfigs for a given bucket.
//
// Deprecated: ListNotificationConfigs may be removed in a future version.
func (c *Client) ListNotificationConfigs(ctx context.Context, req *storagepb.ListNotificationConfigsRequest, opts ...gax.CallOption) *NotificationConfigIterator {
return c.internalClient.ListNotificationConfigs(ctx, req, opts...)
// MoveObject moves the source object to the destination object in the same bucket.
func (c *Client) MoveObject(ctx context.Context, req *storagepb.MoveObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
return c.internalClient.MoveObject(ctx, req, opts...)
}
// gRPCClient is a client for interacting with Cloud Storage API over gRPC transport.
@ -916,6 +805,8 @@ type gRPCClient struct {
// The x-goog-* metadata to be sent with each request.
xGoogHeaders []string
logger *slog.Logger
}
// NewClient creates a new storage client based on gRPC.
@ -963,6 +854,7 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error
connPool: connPool,
client: storagepb.NewStorageClient(connPool),
CallOptions: &client.CallOptions,
logger: internaloption.GetLogger(opts),
}
c.setGoogleClientInfo()
@ -1013,7 +905,7 @@ func (c *gRPCClient) DeleteBucket(ctx context.Context, req *storagepb.DeleteBuck
opts = append((*c.CallOptions).DeleteBucket[0:len((*c.CallOptions).DeleteBucket):len((*c.CallOptions).DeleteBucket)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteBucket(ctx, req, settings.GRPC...)
_, err = executeRPC(ctx, c.client.DeleteBucket, req, settings.GRPC, c.logger, "DeleteBucket")
return err
}, opts...)
return err
@ -1037,7 +929,7 @@ func (c *gRPCClient) GetBucket(ctx context.Context, req *storagepb.GetBucketRequ
var resp *storagepb.Bucket
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetBucket(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.GetBucket, req, settings.GRPC, c.logger, "GetBucket")
return err
}, opts...)
if err != nil {
@ -1067,7 +959,7 @@ func (c *gRPCClient) CreateBucket(ctx context.Context, req *storagepb.CreateBuck
var resp *storagepb.Bucket
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateBucket(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.CreateBucket, req, settings.GRPC, c.logger, "CreateBucket")
return err
}, opts...)
if err != nil {
@ -1105,7 +997,7 @@ func (c *gRPCClient) ListBuckets(ctx context.Context, req *storagepb.ListBuckets
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListBuckets(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.ListBuckets, req, settings.GRPC, c.logger, "ListBuckets")
return err
}, opts...)
if err != nil {
@ -1149,7 +1041,7 @@ func (c *gRPCClient) LockBucketRetentionPolicy(ctx context.Context, req *storage
var resp *storagepb.Bucket
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.LockBucketRetentionPolicy(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.LockBucketRetentionPolicy, req, settings.GRPC, c.logger, "LockBucketRetentionPolicy")
return err
}, opts...)
if err != nil {
@ -1176,7 +1068,7 @@ func (c *gRPCClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRe
var resp *iampb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetIamPolicy(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.GetIamPolicy, req, settings.GRPC, c.logger, "GetIamPolicy")
return err
}, opts...)
if err != nil {
@ -1203,7 +1095,7 @@ func (c *gRPCClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRe
var resp *iampb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.SetIamPolicy(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.SetIamPolicy, req, settings.GRPC, c.logger, "SetIamPolicy")
return err
}, opts...)
if err != nil {
@ -1236,7 +1128,7 @@ func (c *gRPCClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamP
var resp *iampb.TestIamPermissionsResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.TestIamPermissions(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.TestIamPermissions, req, settings.GRPC, c.logger, "TestIamPermissions")
return err
}, opts...)
if err != nil {
@ -1263,7 +1155,7 @@ func (c *gRPCClient) UpdateBucket(ctx context.Context, req *storagepb.UpdateBuck
var resp *storagepb.Bucket
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.UpdateBucket(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.UpdateBucket, req, settings.GRPC, c.logger, "UpdateBucket")
return err
}, opts...)
if err != nil {
@ -1290,7 +1182,7 @@ func (c *gRPCClient) ComposeObject(ctx context.Context, req *storagepb.ComposeOb
var resp *storagepb.Object
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ComposeObject(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.ComposeObject, req, settings.GRPC, c.logger, "ComposeObject")
return err
}, opts...)
if err != nil {
@ -1316,7 +1208,7 @@ func (c *gRPCClient) DeleteObject(ctx context.Context, req *storagepb.DeleteObje
opts = append((*c.CallOptions).DeleteObject[0:len((*c.CallOptions).DeleteObject):len((*c.CallOptions).DeleteObject)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteObject(ctx, req, settings.GRPC...)
_, err = executeRPC(ctx, c.client.DeleteObject, req, settings.GRPC, c.logger, "DeleteObject")
return err
}, opts...)
return err
@ -1340,7 +1232,7 @@ func (c *gRPCClient) RestoreObject(ctx context.Context, req *storagepb.RestoreOb
var resp *storagepb.Object
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.RestoreObject(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.RestoreObject, req, settings.GRPC, c.logger, "RestoreObject")
return err
}, opts...)
if err != nil {
@ -1367,7 +1259,7 @@ func (c *gRPCClient) CancelResumableWrite(ctx context.Context, req *storagepb.Ca
var resp *storagepb.CancelResumableWriteResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CancelResumableWrite(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.CancelResumableWrite, req, settings.GRPC, c.logger, "CancelResumableWrite")
return err
}, opts...)
if err != nil {
@ -1394,7 +1286,7 @@ func (c *gRPCClient) GetObject(ctx context.Context, req *storagepb.GetObjectRequ
var resp *storagepb.Object
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetObject(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.GetObject, req, settings.GRPC, c.logger, "GetObject")
return err
}, opts...)
if err != nil {
@ -1421,7 +1313,26 @@ func (c *gRPCClient) ReadObject(ctx context.Context, req *storagepb.ReadObjectRe
var resp storagepb.Storage_ReadObjectClient
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "ReadObject")
resp, err = c.client.ReadObject(ctx, req, settings.GRPC...)
c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "ReadObject")
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *gRPCClient) BidiReadObject(ctx context.Context, opts ...gax.CallOption) (storagepb.Storage_BidiReadObjectClient, error) {
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...)
var resp storagepb.Storage_BidiReadObjectClient
opts = append((*c.CallOptions).BidiReadObject[0:len((*c.CallOptions).BidiReadObject):len((*c.CallOptions).BidiReadObject)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "BidiReadObject")
resp, err = c.client.BidiReadObject(ctx, settings.GRPC...)
c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "BidiReadObject")
return err
}, opts...)
if err != nil {
@ -1448,7 +1359,7 @@ func (c *gRPCClient) UpdateObject(ctx context.Context, req *storagepb.UpdateObje
var resp *storagepb.Object
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.UpdateObject(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.UpdateObject, req, settings.GRPC, c.logger, "UpdateObject")
return err
}, opts...)
if err != nil {
@ -1463,7 +1374,9 @@ func (c *gRPCClient) WriteObject(ctx context.Context, opts ...gax.CallOption) (s
opts = append((*c.CallOptions).WriteObject[0:len((*c.CallOptions).WriteObject):len((*c.CallOptions).WriteObject)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "WriteObject")
resp, err = c.client.WriteObject(ctx, settings.GRPC...)
c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "WriteObject")
return err
}, opts...)
if err != nil {
@ -1478,7 +1391,9 @@ func (c *gRPCClient) BidiWriteObject(ctx context.Context, opts ...gax.CallOption
opts = append((*c.CallOptions).BidiWriteObject[0:len((*c.CallOptions).BidiWriteObject):len((*c.CallOptions).BidiWriteObject)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "BidiWriteObject")
resp, err = c.client.BidiWriteObject(ctx, settings.GRPC...)
c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "BidiWriteObject")
return err
}, opts...)
if err != nil {
@ -1516,7 +1431,7 @@ func (c *gRPCClient) ListObjects(ctx context.Context, req *storagepb.ListObjects
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListObjects(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.ListObjects, req, settings.GRPC, c.logger, "ListObjects")
return err
}, opts...)
if err != nil {
@ -1563,7 +1478,7 @@ func (c *gRPCClient) RewriteObject(ctx context.Context, req *storagepb.RewriteOb
var resp *storagepb.RewriteResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.RewriteObject(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.RewriteObject, req, settings.GRPC, c.logger, "RewriteObject")
return err
}, opts...)
if err != nil {
@ -1590,7 +1505,7 @@ func (c *gRPCClient) StartResumableWrite(ctx context.Context, req *storagepb.Sta
var resp *storagepb.StartResumableWriteResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.StartResumableWrite(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.StartResumableWrite, req, settings.GRPC, c.logger, "StartResumableWrite")
return err
}, opts...)
if err != nil {
@ -1617,7 +1532,7 @@ func (c *gRPCClient) QueryWriteStatus(ctx context.Context, req *storagepb.QueryW
var resp *storagepb.QueryWriteStatusResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.QueryWriteStatus(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.QueryWriteStatus, req, settings.GRPC, c.logger, "QueryWriteStatus")
return err
}, opts...)
if err != nil {
@ -1626,11 +1541,11 @@ func (c *gRPCClient) QueryWriteStatus(ctx context.Context, req *storagepb.QueryW
return resp, nil
}
func (c *gRPCClient) GetServiceAccount(ctx context.Context, req *storagepb.GetServiceAccountRequest, opts ...gax.CallOption) (*storagepb.ServiceAccount, error) {
func (c *gRPCClient) MoveObject(ctx context.Context, req *storagepb.MoveObjectRequest, opts ...gax.CallOption) (*storagepb.Object, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])
if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetBucket()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])) > 0 {
routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
@ -1640,11 +1555,11 @@ func (c *gRPCClient) GetServiceAccount(ctx context.Context, req *storagepb.GetSe
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).GetServiceAccount[0:len((*c.CallOptions).GetServiceAccount):len((*c.CallOptions).GetServiceAccount)], opts...)
var resp *storagepb.ServiceAccount
opts = append((*c.CallOptions).MoveObject[0:len((*c.CallOptions).MoveObject):len((*c.CallOptions).MoveObject)], opts...)
var resp *storagepb.Object
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetServiceAccount(ctx, req, settings.GRPC...)
resp, err = executeRPC(ctx, c.client.MoveObject, req, settings.GRPC, c.logger, "MoveObject")
return err
}, opts...)
if err != nil {
@ -1652,294 +1567,3 @@ func (c *gRPCClient) GetServiceAccount(ctx context.Context, req *storagepb.GetSe
}
return resp, nil
}
func (c *gRPCClient) CreateHmacKey(ctx context.Context, req *storagepb.CreateHmacKeyRequest, opts ...gax.CallOption) (*storagepb.CreateHmacKeyResponse, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).CreateHmacKey[0:len((*c.CallOptions).CreateHmacKey):len((*c.CallOptions).CreateHmacKey)], opts...)
var resp *storagepb.CreateHmacKeyResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateHmacKey(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *gRPCClient) DeleteHmacKey(ctx context.Context, req *storagepb.DeleteHmacKeyRequest, opts ...gax.CallOption) error {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).DeleteHmacKey[0:len((*c.CallOptions).DeleteHmacKey):len((*c.CallOptions).DeleteHmacKey)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteHmacKey(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
func (c *gRPCClient) GetHmacKey(ctx context.Context, req *storagepb.GetHmacKeyRequest, opts ...gax.CallOption) (*storagepb.HmacKeyMetadata, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).GetHmacKey[0:len((*c.CallOptions).GetHmacKey):len((*c.CallOptions).GetHmacKey)], opts...)
var resp *storagepb.HmacKeyMetadata
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetHmacKey(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *gRPCClient) ListHmacKeys(ctx context.Context, req *storagepb.ListHmacKeysRequest, opts ...gax.CallOption) *HmacKeyMetadataIterator {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetProject())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).ListHmacKeys[0:len((*c.CallOptions).ListHmacKeys):len((*c.CallOptions).ListHmacKeys)], opts...)
it := &HmacKeyMetadataIterator{}
req = proto.Clone(req).(*storagepb.ListHmacKeysRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*storagepb.HmacKeyMetadata, string, error) {
resp := &storagepb.ListHmacKeysResponse{}
if pageToken != "" {
req.PageToken = pageToken
}
if pageSize > math.MaxInt32 {
req.PageSize = math.MaxInt32
} else if pageSize != 0 {
req.PageSize = int32(pageSize)
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListHmacKeys(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, "", err
}
it.Response = resp
return resp.GetHmacKeys(), resp.GetNextPageToken(), nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
if err != nil {
return "", err
}
it.items = append(it.items, items...)
return nextPageToken, nil
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.GetPageSize())
it.pageInfo.Token = req.GetPageToken()
return it
}
func (c *gRPCClient) UpdateHmacKey(ctx context.Context, req *storagepb.UpdateHmacKeyRequest, opts ...gax.CallOption) (*storagepb.HmacKeyMetadata, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<project>.*)"); reg.MatchString(req.GetHmacKey().GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetHmacKey().GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetHmacKey().GetProject())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).UpdateHmacKey[0:len((*c.CallOptions).UpdateHmacKey):len((*c.CallOptions).UpdateHmacKey)], opts...)
var resp *storagepb.HmacKeyMetadata
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.UpdateHmacKey(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *gRPCClient) DeleteNotificationConfig(ctx context.Context, req *storagepb.DeleteNotificationConfigRequest, opts ...gax.CallOption) error {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)(?:/.*)?"); reg.MatchString(req.GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])) > 0 {
routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).DeleteNotificationConfig[0:len((*c.CallOptions).DeleteNotificationConfig):len((*c.CallOptions).DeleteNotificationConfig)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteNotificationConfig(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
func (c *gRPCClient) GetNotificationConfig(ctx context.Context, req *storagepb.GetNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)(?:/.*)?"); reg.MatchString(req.GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])) > 0 {
routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).GetNotificationConfig[0:len((*c.CallOptions).GetNotificationConfig):len((*c.CallOptions).GetNotificationConfig)], opts...)
var resp *storagepb.NotificationConfig
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetNotificationConfig(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *gRPCClient) CreateNotificationConfig(ctx context.Context, req *storagepb.CreateNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).CreateNotificationConfig[0:len((*c.CallOptions).CreateNotificationConfig):len((*c.CallOptions).CreateNotificationConfig)], opts...)
var resp *storagepb.NotificationConfig
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateNotificationConfig(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *gRPCClient) ListNotificationConfigs(ctx context.Context, req *storagepb.ListNotificationConfigsRequest, opts ...gax.CallOption) *NotificationConfigIterator {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
routingHeadersMap["bucket"] = url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
routingHeaders = strings.TrimSuffix(routingHeaders, "&")
hds := []string{"x-goog-request-params", routingHeaders}
hds = append(c.xGoogHeaders, hds...)
ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)
opts = append((*c.CallOptions).ListNotificationConfigs[0:len((*c.CallOptions).ListNotificationConfigs):len((*c.CallOptions).ListNotificationConfigs)], opts...)
it := &NotificationConfigIterator{}
req = proto.Clone(req).(*storagepb.ListNotificationConfigsRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*storagepb.NotificationConfig, string, error) {
resp := &storagepb.ListNotificationConfigsResponse{}
if pageToken != "" {
req.PageToken = pageToken
}
if pageSize > math.MaxInt32 {
req.PageSize = math.MaxInt32
} else if pageSize != 0 {
req.PageSize = int32(pageSize)
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListNotificationConfigs(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, "", err
}
it.Response = resp
return resp.GetNotificationConfigs(), resp.GetNextPageToken(), nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
if err != nil {
return "", err
}
it.items = append(it.items, items...)
return nextPageToken, nil
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.GetPageSize())
it.pageInfo.Token = req.GetPageToken()
return it
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
// Copyright 2024 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.
// All options in this package are experimental.
package internal
var (
// WithMetricInterval is a function which is implemented by storage package.
// It sets how often to emit metrics when using NewPeriodicReader and must be
// greater than 1 minute.
WithMetricInterval any // func (*time.Duration) option.ClientOption
// WithMetricExporter is a function which is implemented by storage package.
// Set an alternate client-side metric Exporter to emit metrics through.
WithMetricExporter any // func (*metric.Exporter) option.ClientOption
// WithReadStallTimeout is a function which is implemented by storage package.
// It takes ReadStallTimeoutConfig as inputs and returns a option.ClientOption.
WithReadStallTimeout any // func (*ReadStallTimeoutConfig) option.ClientOption
// WithGRPCBidiReads is a function which is implemented by the storage package.
// It sets the gRPC client to use the BidiReadObject API for downloads.
WithGRPCBidiReads any // func() option.ClientOption
)

View file

@ -15,4 +15,4 @@
package internal
// Version is the current tagged release of the library.
const Version = "1.44.0"
const Version = "1.50.0"

View file

@ -15,16 +15,74 @@
package storage
import (
"os"
"strconv"
"time"
"cloud.google.com/go/storage/experimental"
storageinternal "cloud.google.com/go/storage/internal"
"go.opentelemetry.io/otel/sdk/metric"
"google.golang.org/api/option"
"google.golang.org/api/option/internaloption"
)
// storageConfig contains the Storage client option configuration that can be
const (
dynamicReadReqIncreaseRateEnv = "DYNAMIC_READ_REQ_INCREASE_RATE"
dynamicReadReqInitialTimeoutEnv = "DYNAMIC_READ_REQ_INITIAL_TIMEOUT"
defaultDynamicReadReqIncreaseRate = 15.0
defaultDynamicReqdReqMaxTimeout = 1 * time.Hour
defaultDynamicReadReqMinTimeout = 500 * time.Millisecond
defaultTargetPercentile = 0.99
)
func init() {
// initialize experimental options
storageinternal.WithMetricExporter = withMetricExporter
storageinternal.WithMetricInterval = withMetricInterval
storageinternal.WithReadStallTimeout = withReadStallTimeout
storageinternal.WithGRPCBidiReads = withGRPCBidiReads
}
// getDynamicReadReqIncreaseRateFromEnv returns the value set in the env variable.
// It returns defaultDynamicReadReqIncreaseRate if env is not set or the set value is invalid.
func getDynamicReadReqIncreaseRateFromEnv() float64 {
increaseRate := os.Getenv(dynamicReadReqIncreaseRateEnv)
if increaseRate == "" {
return defaultDynamicReadReqIncreaseRate
}
val, err := strconv.ParseFloat(increaseRate, 64)
if err != nil {
return defaultDynamicReadReqIncreaseRate
}
return val
}
// getDynamicReadReqInitialTimeoutSecFromEnv returns the value set in the env variable.
// It returns the passed defaultVal if env is not set or the set value is invalid.
func getDynamicReadReqInitialTimeoutSecFromEnv(defaultVal time.Duration) time.Duration {
initialTimeout := os.Getenv(dynamicReadReqInitialTimeoutEnv)
if initialTimeout == "" {
return defaultVal
}
val, err := time.ParseDuration(initialTimeout)
if err != nil {
return defaultVal
}
return val
}
// set through storageClientOptions.
type storageConfig struct {
useJSONforReads bool
readAPIWasSet bool
disableClientMetrics bool
useJSONforReads bool
readAPIWasSet bool
disableClientMetrics bool
metricExporter *metric.Exporter
metricInterval time.Duration
manualReader *metric.ManualReader
readStallTimeoutConfig *experimental.ReadStallTimeoutConfig
grpcBidiReads bool
}
// newStorageConfig generates a new storageConfig with all the given
@ -108,3 +166,91 @@ func WithDisabledClientMetrics() option.ClientOption {
func (w *withDisabledClientMetrics) ApplyStorageOpt(c *storageConfig) {
c.disableClientMetrics = w.disabledClientMetrics
}
type withMeterOptions struct {
internaloption.EmbeddableAdapter
// set sampling interval
interval time.Duration
}
func withMetricInterval(interval time.Duration) option.ClientOption {
return &withMeterOptions{interval: interval}
}
func (w *withMeterOptions) ApplyStorageOpt(c *storageConfig) {
c.metricInterval = w.interval
}
type withMetricExporterConfig struct {
internaloption.EmbeddableAdapter
// exporter override
metricExporter *metric.Exporter
}
func withMetricExporter(ex *metric.Exporter) option.ClientOption {
return &withMetricExporterConfig{metricExporter: ex}
}
func (w *withMetricExporterConfig) ApplyStorageOpt(c *storageConfig) {
c.metricExporter = w.metricExporter
}
type withTestMetricReaderConfig struct {
internaloption.EmbeddableAdapter
// reader override
metricReader *metric.ManualReader
}
func withTestMetricReader(ex *metric.ManualReader) option.ClientOption {
return &withTestMetricReaderConfig{metricReader: ex}
}
func (w *withTestMetricReaderConfig) ApplyStorageOpt(c *storageConfig) {
c.manualReader = w.metricReader
}
// WithReadStallTimeout is an option that may be passed to [NewClient].
// It enables the client to retry the stalled read request, happens as part of
// storage.Reader creation. As the name suggest, timeout is adjusted dynamically
// based on past observed read-req latencies.
//
// This is only supported for the read operation and that too for http(XML) client.
// Grpc read-operation will be supported soon.
func withReadStallTimeout(rstc *experimental.ReadStallTimeoutConfig) option.ClientOption {
// TODO (raj-prince): To keep separate dynamicDelay instance for different BucketHandle.
// Currently, dynamicTimeout is kept at the client and hence shared across all the
// BucketHandle, which is not the ideal state. As latency depends on location of VM
// and Bucket, and read latency of different buckets may lie in different range.
// Hence having a separate dynamicTimeout instance at BucketHandle level will
// be better
if rstc.Min == time.Duration(0) {
rstc.Min = defaultDynamicReadReqMinTimeout
}
if rstc.TargetPercentile == 0 {
rstc.TargetPercentile = defaultTargetPercentile
}
return &withReadStallTimeoutConfig{
readStallTimeoutConfig: rstc,
}
}
type withReadStallTimeoutConfig struct {
internaloption.EmbeddableAdapter
readStallTimeoutConfig *experimental.ReadStallTimeoutConfig
}
func (wrstc *withReadStallTimeoutConfig) ApplyStorageOpt(config *storageConfig) {
config.readStallTimeoutConfig = wrstc.readStallTimeoutConfig
}
func withGRPCBidiReads() option.ClientOption {
return &withGRPCBidiReadsConfig{}
}
type withGRPCBidiReadsConfig struct {
internaloption.EmbeddableAdapter
}
func (w *withGRPCBidiReadsConfig) ApplyStorageOpt(config *storageConfig) {
config.grpcBidiReads = true
}

View file

@ -22,6 +22,7 @@ import (
"io/ioutil"
"net/http"
"strings"
"sync"
"time"
"cloud.google.com/go/internal/trace"
@ -140,6 +141,7 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
encryptionKey: o.encryptionKey,
conds: o.conds,
readCompressed: o.readCompressed,
handle: &o.readHandle,
}
r, err = o.c.tc.NewRangeReader(ctx, params, opts...)
@ -155,6 +157,49 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
return r, err
}
// NewMultiRangeDownloader creates a multi-range reader for an object.
// Must be called on a gRPC client created using [NewGRPCClient].
//
// This uses the gRPC-specific bi-directional read API, which is in private
// preview; please contact your account manager if interested.
func (o *ObjectHandle) NewMultiRangeDownloader(ctx context.Context) (mrd *MultiRangeDownloader, err error) {
// This span covers the life of the reader. It is closed via the context
// in Reader.Close.
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.MultiRangeDownloader")
if err := o.validate(); err != nil {
return nil, err
}
if o.conds != nil {
if err := o.conds.validate("NewMultiRangeDownloader"); err != nil {
return nil, err
}
}
opts := makeStorageOpts(true, o.retry, o.userProject)
params := &newMultiRangeDownloaderParams{
bucket: o.bucket,
conds: o.conds,
encryptionKey: o.encryptionKey,
gen: o.gen,
object: o.object,
handle: &o.readHandle,
}
r, err := o.c.tc.NewMultiRangeDownloader(ctx, params, opts...)
// Pass the context so that the span can be closed in MultiRangeDownloader.Close(), or close the
// span now if there is an error.
if err == nil {
r.ctx = ctx
} else {
trace.EndSpan(ctx, err)
}
return r, err
}
// decompressiveTranscoding returns true if the request was served decompressed
// and different than its original storage form. This happens when the "Content-Encoding"
// header is "gzip".
@ -222,12 +267,16 @@ var emptyBody = ioutil.NopCloser(strings.NewReader(""))
// the stored CRC, returning an error from Read if there is a mismatch. This integrity check
// is skipped if transcoding occurs. See https://cloud.google.com/storage/docs/transcoding.
type Reader struct {
Attrs ReaderObjectAttrs
Attrs ReaderObjectAttrs
objectMetadata *map[string]string
seen, remain, size int64
checkCRC bool // Did we check the CRC? This is now only used by tests.
reader io.ReadCloser
ctx context.Context
mu sync.Mutex
handle *ReadHandle
}
// Close closes the Reader. It must be called when done reading.
@ -298,3 +347,95 @@ func (r *Reader) CacheControl() string {
func (r *Reader) LastModified() (time.Time, error) {
return r.Attrs.LastModified, nil
}
// Metadata returns user-provided metadata, in key/value pairs.
//
// It can be nil if no metadata is present, or if the client uses the JSON
// API for downloads. Only the XML and gRPC APIs support getting
// custom metadata via the Reader; for JSON make a separate call to
// ObjectHandle.Attrs.
func (r *Reader) Metadata() map[string]string {
if r.objectMetadata != nil {
return *r.objectMetadata
}
return nil
}
// ReadHandle returns the read handle associated with an object.
// ReadHandle will be periodically refreshed.
//
// ReadHandle requires the gRPC-specific bi-directional read API, which is in
// private preview; please contact your account manager if interested.
// Note that this only valid for gRPC and only with zonal buckets.
func (r *Reader) ReadHandle() ReadHandle {
if r.handle == nil {
r.handle = &ReadHandle{}
}
r.mu.Lock()
defer r.mu.Unlock()
return (*r.handle)
}
// MultiRangeDownloader reads a Cloud Storage object.
//
// Typically, a MultiRangeDownloader opens a stream to which we can add
// different ranges to read from the object.
//
// This API is currently in preview and is not yet available for general use.
type MultiRangeDownloader struct {
Attrs ReaderObjectAttrs
reader multiRangeDownloader
ctx context.Context
}
type multiRangeDownloader interface {
add(output io.Writer, offset, limit int64, callback func(int64, int64, error))
wait()
close() error
getHandle() []byte
}
// Add adds a new range to MultiRangeDownloader.
//
// The offset for the first byte to return in the read, relative to the start
// of the object.
//
// A negative offset value will be interpreted as the number of bytes from the
// end of the object to be returned. Requesting a negative offset with magnitude
// larger than the size of the object will return the entire object. An offset
// larger than the size of the object will result in an OutOfRange error.
//
// A limit of zero indicates that there is no limit, and a negative limit will
// cause an error.
//
// This will initiate the read range but is non-blocking; call callback to
// process the result. Add is thread-safe and can be called simultaneously
// from different goroutines.
func (mrd *MultiRangeDownloader) Add(output io.Writer, offset, length int64, callback func(int64, int64, error)) {
mrd.reader.add(output, offset, length, callback)
}
// Close the MultiRangeDownloader. It must be called when done reading.
// Adding new ranges after this has been called will cause an error.
//
// This will immediately close the stream and can result in a
// "stream closed early" error if a response for a range is still not processed.
// Call [MultiRangeDownloader.Wait] to avoid this error.
func (mrd *MultiRangeDownloader) Close() error {
err := mrd.reader.close()
trace.EndSpan(mrd.ctx, err)
return err
}
// Wait for all the responses to process on the stream.
// Adding new ranges after this has been called will cause an error.
// Wait will wait for all callbacks to finish.
func (mrd *MultiRangeDownloader) Wait() {
mrd.reader.wait()
}
// GetHandle returns the read handle. This can be used to further speed up the
// follow up read if the same object is read through a different stream.
func (mrd *MultiRangeDownloader) GetHandle() []byte {
return mrd.reader.getHandle()
}

View file

@ -43,6 +43,9 @@ import (
"cloud.google.com/go/storage/internal"
"cloud.google.com/go/storage/internal/apiv2/storagepb"
"github.com/googleapis/gax-go/v2"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"golang.org/x/oauth2/google"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
@ -50,6 +53,8 @@ import (
raw "google.golang.org/api/storage/v1"
"google.golang.org/api/transport"
htransport "google.golang.org/api/transport/http"
"google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/stats/opentelemetry"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/fieldmaskpb"
@ -67,8 +72,8 @@ var (
// errMethodNotSupported indicates that the method called is not currently supported by the client.
// TODO: Export this error when launching the transport-agnostic client.
errMethodNotSupported = errors.New("storage: method is not currently supported")
// errMethodNotValid indicates that given HTTP method is not valid.
errMethodNotValid = fmt.Errorf("storage: HTTP method should be one of %v", reflect.ValueOf(signedURLMethods).MapKeys())
// errSignedURLMethodNotValid indicates that given HTTP method is not valid.
errSignedURLMethodNotValid = fmt.Errorf("storage: HTTP method should be one of %v", reflect.ValueOf(signedURLMethods).MapKeys())
)
var userAgent = fmt.Sprintf("gcloud-golang-storage/%s", internal.Version)
@ -214,14 +219,11 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error
// NewGRPCClient creates a new Storage client using the gRPC transport and API.
// Client methods which have not been implemented in gRPC will return an error.
// In particular, methods for Cloud Pub/Sub notifications are not supported.
// In particular, methods for Cloud Pub/Sub notifications, Service Account HMAC
// keys, and ServiceAccount are not supported.
// Using a non-default universe domain is also not supported with the Storage
// gRPC client.
//
// The storage gRPC API is still in preview and not yet publicly available.
// If you would like to use the API, please first contact your GCP account rep to
// request access. The API may be subject to breaking changes.
//
// Clients should be reused instead of created as needed. The methods of Client
// are safe for concurrent use by multiple goroutines.
//
@ -236,6 +238,62 @@ func NewGRPCClient(ctx context.Context, opts ...option.ClientOption) (*Client, e
return &Client{tc: tc}, nil
}
// CheckDirectConnectivitySupported checks if gRPC direct connectivity
// is available for a specific bucket from the environment where the client
// is running. A `nil` error represents Direct Connectivity was detected.
// Direct connectivity is expected to be available when running from inside
// GCP and connecting to a bucket in the same region.
//
// Experimental helper that's subject to change.
//
// You can pass in [option.ClientOption] you plan on passing to [NewGRPCClient]
func CheckDirectConnectivitySupported(ctx context.Context, bucket string, opts ...option.ClientOption) error {
view := metric.NewView(
metric.Instrument{
Name: "grpc.client.attempt.duration",
Kind: metric.InstrumentKindHistogram,
},
metric.Stream{AttributeFilter: attribute.NewAllowKeysFilter("grpc.lb.locality")},
)
mr := metric.NewManualReader()
provider := metric.NewMeterProvider(metric.WithReader(mr), metric.WithView(view))
// Provider handles shutting down ManualReader
defer provider.Shutdown(ctx)
mo := opentelemetry.MetricsOptions{
MeterProvider: provider,
Metrics: stats.NewMetrics("grpc.client.attempt.duration"),
OptionalLabels: []string{"grpc.lb.locality"},
}
combinedOpts := append(opts, WithDisabledClientMetrics(), option.WithGRPCDialOption(opentelemetry.DialOption(opentelemetry.Options{MetricsOptions: mo})))
client, err := NewGRPCClient(ctx, combinedOpts...)
if err != nil {
return fmt.Errorf("storage.NewGRPCClient: %w", err)
}
defer client.Close()
if _, err = client.Bucket(bucket).Attrs(ctx); err != nil {
return fmt.Errorf("Bucket.Attrs: %w", err)
}
// Call manual reader to collect metric
rm := metricdata.ResourceMetrics{}
if err = mr.Collect(context.Background(), &rm); err != nil {
return fmt.Errorf("ManualReader.Collect: %w", err)
}
for _, sm := range rm.ScopeMetrics {
for _, m := range sm.Metrics {
if m.Name == "grpc.client.attempt.duration" {
hist := m.Data.(metricdata.Histogram[float64])
for _, d := range hist.DataPoints {
v, present := d.Attributes.Value("grpc.lb.locality")
if present && v.AsString() != "" && v.AsString() != "{}" {
return nil
}
}
}
}
}
return errors.New("storage: direct connectivity not detected")
}
// Close closes the Client.
//
// Close need not be called at program exit.
@ -631,7 +689,7 @@ func validateOptions(opts *SignedURLOptions, now time.Time) error {
}
opts.Method = strings.ToUpper(opts.Method)
if _, ok := signedURLMethods[opts.Method]; !ok {
return errMethodNotValid
return errSignedURLMethodNotValid
}
if opts.Expires.IsZero() {
return errors.New("storage: missing required expires option")
@ -879,6 +937,9 @@ func signedURLV2(bucket, name string, opts *SignedURLOptions) (string, error) {
return u.String(), nil
}
// ReadHandle associated with the object. This is periodically refreshed.
type ReadHandle []byte
// ObjectHandle provides operations on an object in a Google Cloud Storage bucket.
// Use BucketHandle.Object to get a handle.
type ObjectHandle struct {
@ -894,6 +955,23 @@ type ObjectHandle struct {
retry *retryConfig
overrideRetention *bool
softDeleted bool
readHandle ReadHandle
}
// ReadHandle returns a new ObjectHandle that uses the ReadHandle to open the objects.
//
// Objects that have already been opened can be opened an additional time,
// using a read handle returned in the response, at lower latency.
// This produces the exact same object and generation and does not check if
// the generation is still the newest one.
// Note that this will be a noop unless it's set on a gRPC client on buckets with
// bi-directional read API access.
// Also note that you can get a ReadHandle only via calling reader.ReadHandle() on a
// previous read of the same object.
func (o *ObjectHandle) ReadHandle(r ReadHandle) *ObjectHandle {
o2 := *o
o2.readHandle = r
return &o2
}
// ACL provides access to the object's access control list.
@ -1098,6 +1176,38 @@ func (o *ObjectHandle) Restore(ctx context.Context, opts *RestoreOptions) (*Obje
}, sOpts...)
}
// Move changes the name of the object to the destination name.
// It can only be used to rename an object within the same bucket. The
// bucket must have [HierarchicalNamespace] enabled to use this method.
//
// Any preconditions set on the ObjectHandle will be applied for the source
// object. Set preconditions on the destination object using
// [MoveObjectDestination.Conditions].
//
// This API is in preview and is not yet publicly available.
func (o *ObjectHandle) Move(ctx context.Context, destination MoveObjectDestination) (*ObjectAttrs, error) {
if err := o.validate(); err != nil {
return nil, err
}
sOpts := makeStorageOpts(true, o.retry, o.userProject)
return o.c.tc.MoveObject(ctx, &moveObjectParams{
bucket: o.bucket,
srcObject: o.object,
dstObject: destination.Object,
srcConds: o.conds,
dstConds: destination.Conditions,
encryptionKey: o.encryptionKey,
}, sOpts...)
}
// MoveObjectDestination provides the destination object name and (optional) preconditions
// for [ObjectHandle.Move].
type MoveObjectDestination struct {
Object string
Conditions *Conditions
}
// NewWriter returns a storage Writer that writes to the GCS object
// associated with this ObjectHandle.
//
@ -1997,56 +2107,91 @@ func applyConds(method string, gen int64, conds *Conditions, call interface{}) e
return nil
}
func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error {
// applySourceConds modifies the provided call using the conditions in conds.
// call is something that quacks like a *raw.WhateverCall.
// This is specifically for calls like Rewrite and Move which have a source and destination
// object.
func applySourceConds(method string, gen int64, conds *Conditions, call interface{}) error {
cval := reflect.ValueOf(call)
if gen >= 0 {
call.SourceGeneration(gen)
if !setSourceGeneration(cval, gen) {
return fmt.Errorf("storage: %s: source generation not supported", method)
}
}
if conds == nil {
return nil
}
if err := conds.validate("CopyTo source"); err != nil {
if err := conds.validate(method); err != nil {
return err
}
switch {
case conds.GenerationMatch != 0:
call.IfSourceGenerationMatch(conds.GenerationMatch)
if !setIfSourceGenerationMatch(cval, conds.GenerationMatch) {
return fmt.Errorf("storage: %s: ifSourceGenerationMatch not supported", method)
}
case conds.GenerationNotMatch != 0:
call.IfSourceGenerationNotMatch(conds.GenerationNotMatch)
if !setIfSourceGenerationNotMatch(cval, conds.GenerationNotMatch) {
return fmt.Errorf("storage: %s: ifSourceGenerationNotMatch not supported", method)
}
case conds.DoesNotExist:
call.IfSourceGenerationMatch(0)
if !setIfSourceGenerationMatch(cval, int64(0)) {
return fmt.Errorf("storage: %s: DoesNotExist not supported", method)
}
}
switch {
case conds.MetagenerationMatch != 0:
call.IfSourceMetagenerationMatch(conds.MetagenerationMatch)
if !setIfSourceMetagenerationMatch(cval, conds.MetagenerationMatch) {
return fmt.Errorf("storage: %s: ifSourceMetagenerationMatch not supported", method)
}
case conds.MetagenerationNotMatch != 0:
call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch)
if !setIfSourceMetagenerationNotMatch(cval, conds.MetagenerationNotMatch) {
return fmt.Errorf("storage: %s: ifSourceMetagenerationNotMatch not supported", method)
}
}
return nil
}
func applySourceCondsProto(gen int64, conds *Conditions, call *storagepb.RewriteObjectRequest) error {
// applySourceCondsProto validates and attempts to set the conditions on a protobuf
// message using protobuf reflection. This is specifically for RPCs which have separate
// preconditions for source and destination objects (e.g. Rewrite and Move).
func applySourceCondsProto(method string, gen int64, conds *Conditions, msg proto.Message) error {
rmsg := msg.ProtoReflect()
if gen >= 0 {
call.SourceGeneration = gen
if !setConditionProtoField(rmsg, "source_generation", gen) {
return fmt.Errorf("storage: %s: generation not supported", method)
}
}
if conds == nil {
return nil
}
if err := conds.validate("CopyTo source"); err != nil {
if err := conds.validate(method); err != nil {
return err
}
switch {
case conds.GenerationMatch != 0:
call.IfSourceGenerationMatch = proto.Int64(conds.GenerationMatch)
if !setConditionProtoField(rmsg, "if_source_generation_match", conds.GenerationMatch) {
return fmt.Errorf("storage: %s: ifSourceGenerationMatch not supported", method)
}
case conds.GenerationNotMatch != 0:
call.IfSourceGenerationNotMatch = proto.Int64(conds.GenerationNotMatch)
if !setConditionProtoField(rmsg, "if_source_generation_not_match", conds.GenerationNotMatch) {
return fmt.Errorf("storage: %s: ifSourceGenerationNotMatch not supported", method)
}
case conds.DoesNotExist:
call.IfSourceGenerationMatch = proto.Int64(0)
if !setConditionProtoField(rmsg, "if_source_generation_match", int64(0)) {
return fmt.Errorf("storage: %s: DoesNotExist not supported", method)
}
}
switch {
case conds.MetagenerationMatch != 0:
call.IfSourceMetagenerationMatch = proto.Int64(conds.MetagenerationMatch)
if !setConditionProtoField(rmsg, "if_source_metageneration_match", conds.MetagenerationMatch) {
return fmt.Errorf("storage: %s: ifSourceMetagenerationMatch not supported", method)
}
case conds.MetagenerationNotMatch != 0:
call.IfSourceMetagenerationNotMatch = proto.Int64(conds.MetagenerationNotMatch)
if !setConditionProtoField(rmsg, "if_source_metageneration_not_match", conds.MetagenerationNotMatch) {
return fmt.Errorf("storage: %s: ifSourceMetagenerationNotMatch not supported", method)
}
}
return nil
}
@ -2085,6 +2230,27 @@ func setIfMetagenerationNotMatch(cval reflect.Value, value interface{}) bool {
return setCondition(cval.MethodByName("IfMetagenerationNotMatch"), value)
}
// More methods to set source object precondition fields (used by Rewrite and Move APIs).
func setSourceGeneration(cval reflect.Value, value interface{}) bool {
return setCondition(cval.MethodByName("SourceGeneration"), value)
}
func setIfSourceGenerationMatch(cval reflect.Value, value interface{}) bool {
return setCondition(cval.MethodByName("IfSourceGenerationMatch"), value)
}
func setIfSourceGenerationNotMatch(cval reflect.Value, value interface{}) bool {
return setCondition(cval.MethodByName("IfSourceGenerationNotMatch"), value)
}
func setIfSourceMetagenerationMatch(cval reflect.Value, value interface{}) bool {
return setCondition(cval.MethodByName("IfSourceMetagenerationMatch"), value)
}
func setIfSourceMetagenerationNotMatch(cval reflect.Value, value interface{}) bool {
return setCondition(cval.MethodByName("IfSourceMetagenerationNotMatch"), value)
}
func setCondition(setter reflect.Value, value interface{}) bool {
if setter.IsValid() {
setter.Call([]reflect.Value{reflect.ValueOf(value)})

View file

@ -88,11 +88,29 @@ type Writer struct {
// cancellation.
ChunkRetryDeadline time.Duration
// ChunkTransferTimeout sets a per-chunk request timeout for resumable uploads.
//
// For resumable uploads, the Writer will terminate the request and attempt a retry
// if the request to upload a particular chunk stalls for longer than this duration. Retries
// may continue until the ChunkRetryDeadline is reached.
//
// The default value is no timeout.
ChunkTransferTimeout time.Duration
// ForceEmptyContentType is an optional parameter that is used to disable
// auto-detection of Content-Type. By default, if a blank Content-Type
// is provided, then gax.DetermineContentType is called to sniff the type.
ForceEmptyContentType bool
// Append is a parameter to indicate whether the writer should use appendable
// object semantics for the new object generation. Appendable objects are
// visible on the first Write() call, and can be appended to until they are
// finalized. The object is finalized on a call to Close().
//
// Append is only supported for gRPC. This feature is in preview and is not
// yet available for general use.
Append bool
// ProgressFunc can be used to monitor the progress of a large write
// operation. If ProgressFunc is not nil and writing requires multiple
// calls to the underlying service (see
@ -188,11 +206,13 @@ func (w *Writer) openWriter() (err error) {
ctx: w.ctx,
chunkSize: w.ChunkSize,
chunkRetryDeadline: w.ChunkRetryDeadline,
chunkTransferTimeout: w.ChunkTransferTimeout,
bucket: w.o.bucket,
attrs: &w.ObjectAttrs,
conds: w.o.conds,
encryptionKey: w.o.encryptionKey,
sendCRC32C: w.SendCRC32C,
append: w.Append,
donec: w.donec,
setError: w.error,
progress: w.progress,

View file

@ -13,6 +13,9 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Golang/Intellij
.idea
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

View file

@ -44,13 +44,21 @@ Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the
## Status
It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, Microsoft, etc](https://github.com/imdario/mergo#mergo-in-the-wild).
Mergo is stable and frozen, ready for production. Check a short list of the projects using at large scale it [here](https://github.com/imdario/mergo#mergo-in-the-wild).
No new features are accepted. They will be considered for a future v2 that improves the implementation and fixes bugs for corner cases.
### Important notes
#### 1.0.0
In [1.0.0](//github.com/imdario/mergo/releases/tag/1.0.0) Mergo moves to a vanity URL `dario.cat/mergo`.
In [1.0.0](//github.com/imdario/mergo/releases/tag/1.0.0) Mergo moves to a vanity URL `dario.cat/mergo`. No more v1 versions will be released.
If the vanity URL is causing issues in your project due to a dependency pulling Mergo - it isn't a direct dependency in your project - it is recommended to use [replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to pin the version to the last one with the old import URL:
```
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16
```
#### 0.3.9
@ -64,55 +72,24 @@ If you were using Mergo before April 6th, 2015, please check your project works
If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes:
<a href='https://ko-fi.com/B0B58839' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
<a href="https://liberapay.com/dario/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
<a href='https://github.com/sponsors/imdario' target='_blank'><img alt="Become my sponsor" src="https://img.shields.io/github/sponsors/imdario?style=for-the-badge" /></a>
### Mergo in the wild
- [moby/moby](https://github.com/moby/moby)
- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
- [vmware/dispatch](https://github.com/vmware/dispatch)
- [Shopify/themekit](https://github.com/Shopify/themekit)
- [imdario/zas](https://github.com/imdario/zas)
- [matcornic/hermes](https://github.com/matcornic/hermes)
- [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go)
- [kataras/iris](https://github.com/kataras/iris)
- [michaelsauter/crane](https://github.com/michaelsauter/crane)
- [go-task/task](https://github.com/go-task/task)
- [sensu/uchiwa](https://github.com/sensu/uchiwa)
- [ory/hydra](https://github.com/ory/hydra)
- [sisatech/vcli](https://github.com/sisatech/vcli)
- [dairycart/dairycart](https://github.com/dairycart/dairycart)
- [projectcalico/felix](https://github.com/projectcalico/felix)
- [resin-os/balena](https://github.com/resin-os/balena)
- [go-kivik/kivik](https://github.com/go-kivik/kivik)
- [Telefonica/govice](https://github.com/Telefonica/govice)
- [supergiant/supergiant](supergiant/supergiant)
- [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce)
- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
- [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel)
- [EagerIO/Stout](https://github.com/EagerIO/Stout)
- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
- [russross/canvasassignments](https://github.com/russross/canvasassignments)
- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api)
- [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
- [divshot/gitling](https://github.com/divshot/gitling)
- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
- [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
- [elwinar/rambler](https://github.com/elwinar/rambler)
- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman)
- [jfbus/impressionist](https://github.com/jfbus/impressionist)
- [Jmeyering/zealot](https://github.com/Jmeyering/zealot)
- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host)
- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
- [thoas/picfit](https://github.com/thoas/picfit)
- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
- [jnuthong/item_search](https://github.com/jnuthong/item_search)
- [bukalapak/snowboard](https://github.com/bukalapak/snowboard)
- [containerssh/containerssh](https://github.com/containerssh/containerssh)
- [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
- [tjpnz/structbot](https://github.com/tjpnz/structbot)
Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/dependents) [of](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.16/dependents) [projects](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.12), including:
* [containerd/containerd](https://github.com/containerd/containerd)
* [datadog/datadog-agent](https://github.com/datadog/datadog-agent)
* [docker/cli/](https://github.com/docker/cli/)
* [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
* [go-micro/go-micro](https://github.com/go-micro/go-micro)
* [grafana/loki](https://github.com/grafana/loki)
* [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
* [masterminds/sprig](github.com/Masterminds/sprig)
* [moby/moby](https://github.com/moby/moby)
* [slackhq/nebula](https://github.com/slackhq/nebula)
* [volcano-sh/volcano](https://github.com/volcano-sh/volcano)
## Install
@ -141,6 +118,39 @@ if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
}
```
If you need to override pointers, so the source pointer's value is assigned to the destination's pointer, you must use `WithoutDereference`:
```go
package main
import (
"fmt"
"dario.cat/mergo"
)
type Foo struct {
A *string
B int64
}
func main() {
first := "first"
second := "second"
src := Foo{
A: &first,
B: 2,
}
dest := Foo{
A: &second,
B: 1,
}
mergo.Merge(&dest, src, mergo.WithOverride, mergo.WithoutDereference)
}
```
Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field.
```go

View file

@ -58,7 +58,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf
}
fieldName := field.Name
fieldName = changeInitialCase(fieldName, unicode.ToLower)
if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v), !config.ShouldNotDereference) || overwrite) {
if _, ok := dstMap[fieldName]; !ok || (!isEmptyValue(reflect.ValueOf(src.Field(i).Interface()), !config.ShouldNotDereference) && overwrite) || config.overwriteWithEmptyValue {
dstMap[fieldName] = src.Field(i).Interface()
}
}

View file

@ -269,7 +269,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
} else {
} else if src.Elem().Kind() != reflect.Struct {
if overwriteWithEmptySrc || (overwrite && !src.IsNil()) || dst.IsNil() {
dst.Set(src)
}

View file

@ -1,5 +1,12 @@
# Release History
## 1.17.0 (2025-01-07)
### Features Added
* Added field `OperationLocationResultPath` to `runtime.NewPollerOptions[T]` for LROs that use the `Operation-Location` pattern.
* Support `encoding.TextMarshaler` and `encoding.TextUnmarshaler` interfaces in `arm.ResourceID`.
## 1.16.0 (2024-10-17)
### Features Added

View file

@ -110,6 +110,21 @@ func (id *ResourceID) String() string {
return id.stringValue
}
// MarshalText returns a textual representation of the ResourceID
func (id *ResourceID) MarshalText() ([]byte, error) {
return []byte(id.String()), nil
}
// UnmarshalText decodes the textual representation of a ResourceID
func (id *ResourceID) UnmarshalText(text []byte) error {
newId, err := ParseResourceID(string(text))
if err != nil {
return err
}
*id = *newId
return nil
}
func newResourceID(parent *ResourceID, resourceTypeName string, resourceName string) *ResourceID {
id := &ResourceID{}
id.init(parent, chooseResourceType(resourceTypeName, parent), resourceName, true)

View file

@ -40,12 +40,13 @@ type Poller[T any] struct {
OrigURL string `json:"origURL"`
Method string `json:"method"`
FinalState pollers.FinalStateVia `json:"finalState"`
ResultPath string `json:"resultPath"`
CurState string `json:"state"`
}
// New creates a new Poller from the provided initial response.
// Pass nil for response to create an empty Poller for rehydration.
func New[T any](pl exported.Pipeline, resp *http.Response, finalState pollers.FinalStateVia) (*Poller[T], error) {
func New[T any](pl exported.Pipeline, resp *http.Response, finalState pollers.FinalStateVia, resultPath string) (*Poller[T], error) {
if resp == nil {
log.Write(log.EventLRO, "Resuming Operation-Location poller.")
return &Poller[T]{pl: pl}, nil
@ -82,6 +83,7 @@ func New[T any](pl exported.Pipeline, resp *http.Response, finalState pollers.Fi
OrigURL: resp.Request.URL.String(),
Method: resp.Request.Method,
FinalState: finalState,
ResultPath: resultPath,
CurState: curState,
}, nil
}
@ -116,10 +118,6 @@ func (p *Poller[T]) Result(ctx context.Context, out *T) error {
var req *exported.Request
var err error
// when the payload is included with the status monitor on
// terminal success it's in the "result" JSON property
payloadPath := "result"
if p.FinalState == pollers.FinalStateViaLocation && p.LocURL != "" {
req, err = exported.NewRequest(ctx, http.MethodGet, p.LocURL)
} else if rl, rlErr := poller.GetResourceLocation(p.resp); rlErr != nil && !errors.Is(rlErr, poller.ErrNoBody) {
@ -138,7 +136,7 @@ func (p *Poller[T]) Result(ctx context.Context, out *T) error {
// if a final GET request has been created, execute it
if req != nil {
// no JSON path when making a final GET request
payloadPath = ""
p.ResultPath = ""
resp, err := p.pl.Do(req)
if err != nil {
return err
@ -146,5 +144,5 @@ func (p *Poller[T]) Result(ctx context.Context, out *T) error {
p.resp = resp
}
return pollers.ResultHelper(p.resp, poller.Failed(p.CurState), payloadPath, out)
return pollers.ResultHelper(p.resp, poller.Failed(p.CurState), p.ResultPath, out)
}

View file

@ -40,5 +40,5 @@ const (
Module = "azcore"
// Version is the semantic version (see http://semver.org) of this module.
Version = "v1.16.0"
Version = "v1.17.0"
)

View file

@ -32,6 +32,7 @@ type PagingHandler[T any] struct {
}
// Pager provides operations for iterating over paged responses.
// Methods on this type are not safe for concurrent use.
type Pager[T any] struct {
current *T
handler PagingHandler[T]

View file

@ -50,8 +50,14 @@ const (
// NewPollerOptions contains the optional parameters for NewPoller.
type NewPollerOptions[T any] struct {
// FinalStateVia contains the final-state-via value for the LRO.
// NOTE: used only for Azure-AsyncOperation and Operation-Location LROs.
FinalStateVia FinalStateVia
// OperationLocationResultPath contains the JSON path to the result's
// payload when it's included with the terminal success response.
// NOTE: only used for Operation-Location LROs.
OperationLocationResultPath string
// Response contains a preconstructed response type.
// The final payload will be unmarshaled into it and returned.
Response *T
@ -98,7 +104,7 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
opr, err = async.New[T](pl, resp, options.FinalStateVia)
} else if op.Applicable(resp) {
// op poller must be checked before loc as it can also have a location header
opr, err = op.New[T](pl, resp, options.FinalStateVia)
opr, err = op.New[T](pl, resp, options.FinalStateVia, options.OperationLocationResultPath)
} else if loc.Applicable(resp) {
opr, err = loc.New[T](pl, resp)
} else if body.Applicable(resp) {
@ -172,7 +178,7 @@ func NewPollerFromResumeToken[T any](token string, pl exported.Pipeline, options
} else if loc.CanResume(asJSON) {
opr, _ = loc.New[T](pl, nil)
} else if op.CanResume(asJSON) {
opr, _ = op.New[T](pl, nil, "")
opr, _ = op.New[T](pl, nil, "", "")
} else {
return nil, fmt.Errorf("unhandled poller token %s", string(raw))
}
@ -200,6 +206,7 @@ type PollingHandler[T any] interface {
}
// Poller encapsulates a long-running operation, providing polling facilities until the operation reaches a terminal state.
// Methods on this type are not safe for concurrent use.
type Poller[T any] struct {
op PollingHandler[T]
resp *http.Response

View file

@ -1,10 +1,23 @@
# Release History
## 1.6.0 (2025-01-23)
### Features Added
* Upgraded service version to `2025-01-05`.
## 1.6.0-beta.1 (2025-01-13)
### Features Added
* Added permissions & resourcetype parameters in listblob response.
* Added BlobProperties field in BlobPrefix definition in listblob response.
### Bugs Fixed
* Fix FilterBlob API if Query contains a space character. Fixes [#23546](https://github.com/Azure/azure-sdk-for-go/issues/23546)
## 1.5.0 (2024-11-13)
### Features Added
* Fix compareHeaders custom sorting algorithm for String To Sign.
* Added permissions & resourcetype parameters in listblob response.
## 1.5.0-beta.1 (2024-10-22)

View file

@ -95,7 +95,7 @@ func (c *Client) ServiceClient() *service.Client {
}
// CreateContainer is a lifecycle method to creates a new container under the specified account.
// If the container with the same name already exists, a ResourceExistsError will be raised.
// If the container with the same name already exists, a ContainerAlreadyExists Error will be raised.
// This method returns a client with which to interact with the newly created container.
func (c *Client) CreateContainer(ctx context.Context, containerName string, o *CreateContainerOptions) (CreateContainerResponse, error) {
return c.svc.CreateContainer(ctx, containerName, o)

View file

@ -8,5 +8,5 @@ package exported
const (
ModuleName = "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
ModuleVersion = "v1.5.0"
ModuleVersion = "v1.6.0"
)

View file

@ -7,7 +7,7 @@ go: true
clear-output-folder: false
version: "^3.0.0"
license-header: MICROSOFT_MIT_NO_VERSION
input-file: "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/f6f50c6388fd5836fa142384641b8353a99874ef/specification/storage/data-plane/Microsoft.BlobStorage/stable/2024-08-04/blob.json"
input-file: "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/ae95eb6a4701d844bada7d1c4f5ecf4a7444e5b8/specification/storage/data-plane/Microsoft.BlobStorage/stable/2025-01-05/blob.json"
credential-scope: "https://storage.azure.com/.default"
output-folder: ../generated
file-prefix: "zz_"
@ -22,6 +22,18 @@ export-clients: true
use: "@autorest/go@4.0.0-preview.65"
```
### Add a Properties field to the BlobPrefix definition
```yaml
directive:
- from: swagger-document
where: $.definitions
transform: >
$.BlobPrefix.properties["Properties"] = {
"type": "object",
"$ref": "#/definitions/BlobPropertiesInternal"
};
```
### Add Owner,Group,Permissions,Acl,ResourceType in ListBlob Response
``` yaml
directive:
@ -29,19 +41,19 @@ directive:
where: $.definitions
transform: >
$.BlobPropertiesInternal.properties["Owner"] = {
"type" : "string",
"type" : "string",
};
$.BlobPropertiesInternal.properties["Group"] = {
"type" : "string",
"type" : "string",
};
$.BlobPropertiesInternal.properties["Permissions"] = {
"type" : "string",
"type" : "string",
};
$.BlobPropertiesInternal.properties["Acl"] = {
"type" : "string",
"type" : "string",
};
$.BlobPropertiesInternal.properties["ResourceType"] = {
"type" : "string",
"type" : "string",
};
```
@ -55,7 +67,7 @@ directive:
$.items.enum.push("permissions");
```
### Updating service version to 2024-11-04
### Updating service version to 2025-01-05
```yaml
directive:
- from:
@ -68,7 +80,7 @@ directive:
where: $
transform: >-
return $.
replaceAll(`[]string{"2024-08-04"}`, `[]string{ServiceVersion}`);
replaceAll(`[]string{"2021-12-02"}`, `[]string{ServiceVersion}`);
```
### Fix CRC Response Header in PutBlob response
@ -410,11 +422,13 @@ directive:
``` yaml
directive:
- from: zz_service_client.go
where: $
transform: >-
return $.
replace(/req.Raw\(\).URL.RawQuery \= reqQP.Encode\(\)/, `req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)`)
- from:
- zz_service_client.go
- zz_container_client.go
where: $
transform: >-
return $.
replace(/req.Raw\(\).URL.RawQuery \= reqQP.Encode\(\)/g, `req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)`);
```
### Change `where` parameter in blob filtering to be required

View file

@ -6,4 +6,4 @@
package generated
const ServiceVersion = "2024-11-04"
const ServiceVersion = "2025-01-05"

View file

@ -29,7 +29,7 @@ type AppendBlobClient struct {
// AppendBlob. Append Block is supported only on version 2015-02-21 version or later.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - body - Initial data
// - options - AppendBlobClientAppendBlockOptions contains the optional parameters for the AppendBlobClient.AppendBlock method.
@ -116,7 +116,13 @@ func (client *AppendBlobClient) appendBlockCreateRequest(ctx context.Context, co
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
if options != nil && options.StructuredBodyType != nil {
req.Raw().Header["x-ms-structured-body"] = []string{*options.StructuredBodyType}
}
if options != nil && options.StructuredContentLength != nil {
req.Raw().Header["x-ms-structured-content-length"] = []string{strconv.FormatInt(*options.StructuredContentLength, 10)}
}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := req.SetBody(body, "application/octet-stream"); err != nil {
return nil, err
}
@ -187,6 +193,9 @@ func (client *AppendBlobClient) appendBlockHandleResponse(resp *http.Response) (
if val := resp.Header.Get("x-ms-request-id"); val != "" {
result.RequestID = &val
}
if val := resp.Header.Get("x-ms-structured-body"); val != "" {
result.StructuredBodyType = &val
}
if val := resp.Header.Get("x-ms-version"); val != "" {
result.Version = &val
}
@ -198,7 +207,7 @@ func (client *AppendBlobClient) appendBlockHandleResponse(resp *http.Response) (
// created with x-ms-blob-type set to AppendBlob. Append Block is supported only on version 2015-02-21 version or later.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - sourceURL - Specify a URL to the copy source.
// - contentLength - The length of the request.
// - options - AppendBlobClientAppendBlockFromURLOptions contains the optional parameters for the AppendBlobClient.AppendBlockFromURL
@ -310,7 +319,7 @@ func (client *AppendBlobClient) appendBlockFromURLCreateRequest(ctx context.Cont
if options != nil && options.SourceRange != nil {
req.Raw().Header["x-ms-source-range"] = []string{*options.SourceRange}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -384,7 +393,7 @@ func (client *AppendBlobClient) appendBlockFromURLHandleResponse(resp *http.Resp
// Create - The Create Append Blob operation creates a new append blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - options - AppendBlobClientCreateOptions contains the optional parameters for the AppendBlobClient.Create method.
// - BlobHTTPHeaders - BlobHTTPHeaders contains a group of parameters for the BlobClient.SetHTTPHeaders method.
@ -494,7 +503,7 @@ func (client *AppendBlobClient) createCreateRequest(ctx context.Context, content
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -557,7 +566,7 @@ func (client *AppendBlobClient) createHandleResponse(resp *http.Response) (Appen
// or later.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - AppendBlobClientSealOptions contains the optional parameters for the AppendBlobClient.Seal method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -615,7 +624,7 @@ func (client *AppendBlobClient) sealCreateRequest(ctx context.Context, options *
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}

View file

@ -29,7 +29,7 @@ type BlobClient struct {
// blob with zero length and full metadata.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - copyID - The copy identifier provided in the x-ms-copy-id header of the original Copy Blob operation.
// - options - BlobClientAbortCopyFromURLOptions contains the optional parameters for the BlobClient.AbortCopyFromURL method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -72,7 +72,7 @@ func (client *BlobClient) abortCopyFromURLCreateRequest(ctx context.Context, cop
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -101,7 +101,7 @@ func (client *BlobClient) abortCopyFromURLHandleResponse(resp *http.Response) (B
// AcquireLease - [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete operations
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - duration - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite
// lease can be between 15 and 60 seconds. A lease duration cannot be changed using
// renew or change.
@ -161,7 +161,7 @@ func (client *BlobClient) acquireLeaseCreateRequest(ctx context.Context, duratio
if options != nil && options.ProposedLeaseID != nil {
req.Raw().Header["x-ms-proposed-lease-id"] = []string{*options.ProposedLeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -203,7 +203,7 @@ func (client *BlobClient) acquireLeaseHandleResponse(resp *http.Response) (BlobC
// BreakLease - [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete operations
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientBreakLeaseOptions contains the optional parameters for the BlobClient.BreakLease method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
func (client *BlobClient) BreakLease(ctx context.Context, options *BlobClientBreakLeaseOptions, modifiedAccessConditions *ModifiedAccessConditions) (BlobClientBreakLeaseResponse, error) {
@ -259,7 +259,7 @@ func (client *BlobClient) breakLeaseCreateRequest(ctx context.Context, options *
if options != nil && options.BreakPeriod != nil {
req.Raw().Header["x-ms-lease-break-period"] = []string{strconv.FormatInt(int64(*options.BreakPeriod), 10)}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -306,7 +306,7 @@ func (client *BlobClient) breakLeaseHandleResponse(resp *http.Response) (BlobCli
// ChangeLease - [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete operations
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - leaseID - Specifies the current lease ID on the resource.
// - proposedLeaseID - Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request) if the proposed
// lease ID is not in the correct format. See Guid Constructor (String) for a list of valid GUID
@ -365,7 +365,7 @@ func (client *BlobClient) changeLeaseCreateRequest(ctx context.Context, leaseID
req.Raw().Header["x-ms-lease-action"] = []string{"change"}
req.Raw().Header["x-ms-lease-id"] = []string{leaseID}
req.Raw().Header["x-ms-proposed-lease-id"] = []string{proposedLeaseID}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -408,7 +408,7 @@ func (client *BlobClient) changeLeaseHandleResponse(resp *http.Response) (BlobCl
// until the copy is complete.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - copySource - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that specifies
// a page blob snapshot. The value should be URL-encoded as it would appear in a request
// URI. The source blob must either be public or must be authenticated via a shared access signature.
@ -517,7 +517,7 @@ func (client *BlobClient) copyFromURLCreateRequest(ctx context.Context, copySour
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -582,7 +582,7 @@ func (client *BlobClient) copyFromURLHandleResponse(resp *http.Response) (BlobCl
// CreateSnapshot - The Create Snapshot operation creates a read-only snapshot of a blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientCreateSnapshotOptions contains the optional parameters for the BlobClient.CreateSnapshot method.
// - CPKInfo - CPKInfo contains a group of parameters for the BlobClient.Download method.
// - CPKScopeInfo - CPKScopeInfo contains a group of parameters for the BlobClient.SetMetadata method.
@ -659,7 +659,7 @@ func (client *BlobClient) createSnapshotCreateRequest(ctx context.Context, optio
}
}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -721,7 +721,7 @@ func (client *BlobClient) createSnapshotHandleResponse(resp *http.Response) (Blo
// return an HTTP status code of 404 (ResourceNotFound).
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientDeleteOptions contains the optional parameters for the BlobClient.Delete method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -788,7 +788,7 @@ func (client *BlobClient) deleteCreateRequest(ctx context.Context, options *Blob
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -817,7 +817,7 @@ func (client *BlobClient) deleteHandleResponse(resp *http.Response) (BlobClientD
// DeleteImmutabilityPolicy - The Delete Immutability Policy operation deletes the immutability policy on the blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientDeleteImmutabilityPolicyOptions contains the optional parameters for the BlobClient.DeleteImmutabilityPolicy
// method.
func (client *BlobClient) DeleteImmutabilityPolicy(ctx context.Context, options *BlobClientDeleteImmutabilityPolicyOptions) (BlobClientDeleteImmutabilityPolicyResponse, error) {
@ -846,15 +846,21 @@ func (client *BlobClient) deleteImmutabilityPolicyCreateRequest(ctx context.Cont
}
reqQP := req.Raw().URL.Query()
reqQP.Set("comp", "immutabilityPolicies")
if options != nil && options.Snapshot != nil {
reqQP.Set("snapshot", *options.Snapshot)
}
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
if options != nil && options.VersionID != nil {
reqQP.Set("versionid", *options.VersionID)
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -884,7 +890,7 @@ func (client *BlobClient) deleteImmutabilityPolicyHandleResponse(resp *http.Resp
// can also call Download to read a snapshot.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientDownloadOptions contains the optional parameters for the BlobClient.Download method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - CPKInfo - CPKInfo contains a group of parameters for the BlobClient.Download method.
@ -965,7 +971,10 @@ func (client *BlobClient) downloadCreateRequest(ctx context.Context, options *Bl
if options != nil && options.RangeGetContentMD5 != nil {
req.Raw().Header["x-ms-range-get-content-md5"] = []string{strconv.FormatBool(*options.RangeGetContentMD5)}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
if options != nil && options.StructuredBodyType != nil {
req.Raw().Header["x-ms-structured-body"] = []string{*options.StructuredBodyType}
}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1173,6 +1182,16 @@ func (client *BlobClient) downloadHandleResponse(resp *http.Response) (BlobClien
if val := resp.Header.Get("x-ms-request-id"); val != "" {
result.RequestID = &val
}
if val := resp.Header.Get("x-ms-structured-body"); val != "" {
result.StructuredBodyType = &val
}
if val := resp.Header.Get("x-ms-structured-content-length"); val != "" {
structuredContentLength, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return BlobClientDownloadResponse{}, err
}
result.StructuredContentLength = &structuredContentLength
}
if val := resp.Header.Get("x-ms-tag-count"); val != "" {
tagCount, err := strconv.ParseInt(val, 10, 64)
if err != nil {
@ -1192,7 +1211,7 @@ func (client *BlobClient) downloadHandleResponse(resp *http.Response) (BlobClien
// GetAccountInfo - Returns the sku name and account kind
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientGetAccountInfoOptions contains the optional parameters for the BlobClient.GetAccountInfo method.
func (client *BlobClient) GetAccountInfo(ctx context.Context, options *BlobClientGetAccountInfoOptions) (BlobClientGetAccountInfoResponse, error) {
var err error
@ -1229,7 +1248,7 @@ func (client *BlobClient) getAccountInfoCreateRequest(ctx context.Context, optio
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1272,7 +1291,7 @@ func (client *BlobClient) getAccountInfoHandleResponse(resp *http.Response) (Blo
// for the blob. It does not return the content of the blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientGetPropertiesOptions contains the optional parameters for the BlobClient.GetProperties method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - CPKInfo - CPKInfo contains a group of parameters for the BlobClient.Download method.
@ -1343,7 +1362,7 @@ func (client *BlobClient) getPropertiesCreateRequest(ctx context.Context, option
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1590,7 +1609,7 @@ func (client *BlobClient) getPropertiesHandleResponse(resp *http.Response) (Blob
// GetTags - The Get Tags operation enables users to get the tags associated with a blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientGetTagsOptions contains the optional parameters for the BlobClient.GetTags method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -1640,7 +1659,7 @@ func (client *BlobClient) getTagsCreateRequest(ctx context.Context, options *Blo
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1672,7 +1691,7 @@ func (client *BlobClient) getTagsHandleResponse(resp *http.Response) (BlobClient
// Query - The Query operation enables users to select/project on blob data by providing simple query expressions.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientQueryOptions contains the optional parameters for the BlobClient.Query method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - CPKInfo - CPKInfo contains a group of parameters for the BlobClient.Download method.
@ -1742,7 +1761,7 @@ func (client *BlobClient) queryCreateRequest(ctx context.Context, options *BlobC
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if options != nil && options.QueryRequest != nil {
if err := runtime.MarshalAsXML(req, *options.QueryRequest); err != nil {
return nil, err
@ -1906,7 +1925,7 @@ func (client *BlobClient) queryHandleResponse(resp *http.Response) (BlobClientQu
// ReleaseLease - [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete operations
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - leaseID - Specifies the current lease ID on the resource.
// - options - BlobClientReleaseLeaseOptions contains the optional parameters for the BlobClient.ReleaseLease method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -1961,7 +1980,7 @@ func (client *BlobClient) releaseLeaseCreateRequest(ctx context.Context, leaseID
}
req.Raw().Header["x-ms-lease-action"] = []string{"release"}
req.Raw().Header["x-ms-lease-id"] = []string{leaseID}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2000,7 +2019,7 @@ func (client *BlobClient) releaseLeaseHandleResponse(resp *http.Response) (BlobC
// RenewLease - [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete operations
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - leaseID - Specifies the current lease ID on the resource.
// - options - BlobClientRenewLeaseOptions contains the optional parameters for the BlobClient.RenewLease method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -2055,7 +2074,7 @@ func (client *BlobClient) renewLeaseCreateRequest(ctx context.Context, leaseID s
}
req.Raw().Header["x-ms-lease-action"] = []string{"renew"}
req.Raw().Header["x-ms-lease-id"] = []string{leaseID}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2097,7 +2116,7 @@ func (client *BlobClient) renewLeaseHandleResponse(resp *http.Response) (BlobCli
// SetExpiry - Sets the time a blob will expire and be deleted.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - expiryOptions - Required. Indicates mode of the expiry time
// - options - BlobClientSetExpiryOptions contains the optional parameters for the BlobClient.SetExpiry method.
func (client *BlobClient) SetExpiry(ctx context.Context, expiryOptions ExpiryOptions, options *BlobClientSetExpiryOptions) (BlobClientSetExpiryResponse, error) {
@ -2138,7 +2157,7 @@ func (client *BlobClient) setExpiryCreateRequest(ctx context.Context, expiryOpti
if options != nil && options.ExpiresOn != nil {
req.Raw().Header["x-ms-expiry-time"] = []string{*options.ExpiresOn}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2177,7 +2196,7 @@ func (client *BlobClient) setExpiryHandleResponse(resp *http.Response) (BlobClie
// SetHTTPHeaders - The Set HTTP Headers operation sets system properties on the blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientSetHTTPHeadersOptions contains the optional parameters for the BlobClient.SetHTTPHeaders method.
// - BlobHTTPHeaders - BlobHTTPHeaders contains a group of parameters for the BlobClient.SetHTTPHeaders method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -2252,7 +2271,7 @@ func (client *BlobClient) setHTTPHeadersCreateRequest(ctx context.Context, optio
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2298,7 +2317,7 @@ func (client *BlobClient) setHTTPHeadersHandleResponse(resp *http.Response) (Blo
// SetImmutabilityPolicy - The Set Immutability Policy operation sets the immutability policy on the blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientSetImmutabilityPolicyOptions contains the optional parameters for the BlobClient.SetImmutabilityPolicy
// method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -2328,9 +2347,15 @@ func (client *BlobClient) setImmutabilityPolicyCreateRequest(ctx context.Context
}
reqQP := req.Raw().URL.Query()
reqQP.Set("comp", "immutabilityPolicies")
if options != nil && options.Snapshot != nil {
reqQP.Set("snapshot", *options.Snapshot)
}
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
if options != nil && options.VersionID != nil {
reqQP.Set("versionid", *options.VersionID)
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfUnmodifiedSince != nil {
@ -2345,7 +2370,7 @@ func (client *BlobClient) setImmutabilityPolicyCreateRequest(ctx context.Context
if options != nil && options.ImmutabilityPolicyExpiry != nil {
req.Raw().Header["x-ms-immutability-policy-until-date"] = []string{(*options.ImmutabilityPolicyExpiry).In(gmt).Format(time.RFC1123)}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2384,7 +2409,7 @@ func (client *BlobClient) setImmutabilityPolicyHandleResponse(resp *http.Respons
// SetLegalHold - The Set Legal Hold operation sets a legal hold on the blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - legalHold - Specified if a legal hold should be set on the blob.
// - options - BlobClientSetLegalHoldOptions contains the optional parameters for the BlobClient.SetLegalHold method.
func (client *BlobClient) SetLegalHold(ctx context.Context, legalHold bool, options *BlobClientSetLegalHoldOptions) (BlobClientSetLegalHoldResponse, error) {
@ -2413,16 +2438,22 @@ func (client *BlobClient) setLegalHoldCreateRequest(ctx context.Context, legalHo
}
reqQP := req.Raw().URL.Query()
reqQP.Set("comp", "legalhold")
if options != nil && options.Snapshot != nil {
reqQP.Set("snapshot", *options.Snapshot)
}
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
if options != nil && options.VersionID != nil {
reqQP.Set("versionid", *options.VersionID)
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-legal-hold"] = []string{strconv.FormatBool(legalHold)}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2459,7 +2490,7 @@ func (client *BlobClient) setLegalHoldHandleResponse(resp *http.Response) (BlobC
// pairs
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientSetMetadataOptions contains the optional parameters for the BlobClient.SetMetadata method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - CPKInfo - CPKInfo contains a group of parameters for the BlobClient.Download method.
@ -2536,7 +2567,7 @@ func (client *BlobClient) setMetadataCreateRequest(ctx context.Context, options
}
}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2591,7 +2622,7 @@ func (client *BlobClient) setMetadataHandleResponse(resp *http.Response) (BlobCl
// SetTags - The Set Tags operation enables users to set tags on a blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - tags - Blob tags
// - options - BlobClientSetTagsOptions contains the optional parameters for the BlobClient.SetTags method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -2645,7 +2676,7 @@ func (client *BlobClient) setTagsCreateRequest(ctx context.Context, tags BlobTag
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := runtime.MarshalAsXML(req, tags); err != nil {
return nil, err
}
@ -2680,7 +2711,7 @@ func (client *BlobClient) setTagsHandleResponse(resp *http.Response) (BlobClient
// storage type. This operation does not update the blob's ETag.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - tier - Indicates the tier to be set on the blob.
// - options - BlobClientSetTierOptions contains the optional parameters for the BlobClient.SetTier method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -2735,7 +2766,7 @@ func (client *BlobClient) setTierCreateRequest(ctx context.Context, tier AccessT
if options != nil && options.RehydratePriority != nil {
req.Raw().Header["x-ms-rehydrate-priority"] = []string{string(*options.RehydratePriority)}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2757,7 +2788,7 @@ func (client *BlobClient) setTierHandleResponse(resp *http.Response) (BlobClient
// StartCopyFromURL - The Start Copy From URL operation copies a blob or an internet resource to a new blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - copySource - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that specifies
// a page blob snapshot. The value should be URL-encoded as it would appear in a request
// URI. The source blob must either be public or must be authenticated via a shared access signature.
@ -2861,7 +2892,7 @@ func (client *BlobClient) startCopyFromURLCreateRequest(ctx context.Context, cop
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -2909,7 +2940,7 @@ func (client *BlobClient) startCopyFromURLHandleResponse(resp *http.Response) (B
// Undelete - Undelete a blob that was previously soft deleted
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - BlobClientUndeleteOptions contains the optional parameters for the BlobClient.Undelete method.
func (client *BlobClient) Undelete(ctx context.Context, options *BlobClientUndeleteOptions) (BlobClientUndeleteResponse, error) {
var err error
@ -2945,7 +2976,7 @@ func (client *BlobClient) undeleteCreateRequest(ctx context.Context, options *Bl
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}

View file

@ -33,7 +33,7 @@ type BlockBlobClient struct {
// belong to.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - blocks - Blob Blocks.
// - options - BlockBlobClientCommitBlockListOptions contains the optional parameters for the BlockBlobClient.CommitBlockList
// method.
@ -152,7 +152,7 @@ func (client *BlockBlobClient) commitBlockListCreateRequest(ctx context.Context,
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := runtime.MarshalAsXML(req, blocks); err != nil {
return nil, err
}
@ -224,7 +224,7 @@ func (client *BlockBlobClient) commitBlockListHandleResponse(resp *http.Response
// GetBlockList - The Get Block List operation retrieves the list of blocks that have been uploaded as part of a block blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - listType - Specifies whether to return the list of committed blocks, the list of uncommitted blocks, or both lists together.
// - options - BlockBlobClientGetBlockListOptions contains the optional parameters for the BlockBlobClient.GetBlockList method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -273,7 +273,7 @@ func (client *BlockBlobClient) getBlockListCreateRequest(ctx context.Context, li
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -329,7 +329,7 @@ func (client *BlockBlobClient) getBlockListHandleResponse(resp *http.Response) (
// Block from URL API in conjunction with Put Block List.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - copySource - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that specifies
// a page blob snapshot. The value should be URL-encoded as it would appear in a request
@ -470,7 +470,7 @@ func (client *BlockBlobClient) putBlobFromURLCreateRequest(ctx context.Context,
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -532,7 +532,7 @@ func (client *BlockBlobClient) putBlobFromURLHandleResponse(resp *http.Response)
// StageBlock - The Stage Block operation creates a new block to be committed as part of a blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - blockID - A valid Base64 string value that identifies the block. Prior to encoding, the string must be less than or equal
// to 64 bytes in size. For a given blob, the length of the value specified for the blockid
// parameter must be the same size for each block.
@ -599,7 +599,13 @@ func (client *BlockBlobClient) stageBlockCreateRequest(ctx context.Context, bloc
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
if options != nil && options.StructuredBodyType != nil {
req.Raw().Header["x-ms-structured-body"] = []string{*options.StructuredBodyType}
}
if options != nil && options.StructuredContentLength != nil {
req.Raw().Header["x-ms-structured-content-length"] = []string{strconv.FormatInt(*options.StructuredContentLength, 10)}
}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := req.SetBody(body, "application/octet-stream"); err != nil {
return nil, err
}
@ -649,6 +655,9 @@ func (client *BlockBlobClient) stageBlockHandleResponse(resp *http.Response) (Bl
if val := resp.Header.Get("x-ms-request-id"); val != "" {
result.RequestID = &val
}
if val := resp.Header.Get("x-ms-structured-body"); val != "" {
result.StructuredBodyType = &val
}
if val := resp.Header.Get("x-ms-version"); val != "" {
result.Version = &val
}
@ -659,7 +668,7 @@ func (client *BlockBlobClient) stageBlockHandleResponse(resp *http.Response) (Bl
// are read from a URL.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - blockID - A valid Base64 string value that identifies the block. Prior to encoding, the string must be less than or equal
// to 64 bytes in size. For a given blob, the length of the value specified for the blockid
// parameter must be the same size for each block.
@ -748,7 +757,7 @@ func (client *BlockBlobClient) stageBlockFromURLCreateRequest(ctx context.Contex
if options != nil && options.SourceRange != nil {
req.Raw().Header["x-ms-source-range"] = []string{*options.SourceRange}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -807,7 +816,7 @@ func (client *BlockBlobClient) stageBlockFromURLHandleResponse(resp *http.Respon
// the content of a block blob, use the Put Block List operation.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - body - Initial data
// - options - BlockBlobClientUploadOptions contains the optional parameters for the BlockBlobClient.Upload method.
@ -924,10 +933,16 @@ func (client *BlockBlobClient) uploadCreateRequest(ctx context.Context, contentL
}
}
}
if options != nil && options.StructuredBodyType != nil {
req.Raw().Header["x-ms-structured-body"] = []string{*options.StructuredBodyType}
}
if options != nil && options.StructuredContentLength != nil {
req.Raw().Header["x-ms-structured-content-length"] = []string{strconv.FormatInt(*options.StructuredContentLength, 10)}
}
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := req.SetBody(body, "application/octet-stream"); err != nil {
return nil, err
}
@ -987,6 +1002,9 @@ func (client *BlockBlobClient) uploadHandleResponse(resp *http.Response) (BlockB
if val := resp.Header.Get("x-ms-request-id"); val != "" {
result.RequestID = &val
}
if val := resp.Header.Get("x-ms-structured-body"); val != "" {
result.StructuredBodyType = &val
}
if val := resp.Header.Get("x-ms-version"); val != "" {
result.Version = &val
}

View file

@ -522,6 +522,7 @@ const (
StorageErrorCodeAuthorizationResourceTypeMismatch StorageErrorCode = "AuthorizationResourceTypeMismatch"
StorageErrorCodeAuthorizationServiceMismatch StorageErrorCode = "AuthorizationServiceMismatch"
StorageErrorCodeAuthorizationSourceIPMismatch StorageErrorCode = "AuthorizationSourceIPMismatch"
StorageErrorCodeBlobAccessTierNotSupportedForAccountType StorageErrorCode = "BlobAccessTierNotSupportedForAccountType"
StorageErrorCodeBlobAlreadyExists StorageErrorCode = "BlobAlreadyExists"
StorageErrorCodeBlobArchived StorageErrorCode = "BlobArchived"
StorageErrorCodeBlobBeingRehydrated StorageErrorCode = "BlobBeingRehydrated"
@ -640,6 +641,7 @@ func PossibleStorageErrorCodeValues() []StorageErrorCode {
StorageErrorCodeAuthorizationResourceTypeMismatch,
StorageErrorCodeAuthorizationServiceMismatch,
StorageErrorCodeAuthorizationSourceIPMismatch,
StorageErrorCodeBlobAccessTierNotSupportedForAccountType,
StorageErrorCodeBlobAlreadyExists,
StorageErrorCodeBlobArchived,
StorageErrorCodeBlobBeingRehydrated,

View file

@ -31,7 +31,7 @@ type ContainerClient struct {
// to 60 seconds, or can be infinite
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - duration - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite
// lease can be between 15 and 60 seconds. A lease duration cannot be changed using
// renew or change.
@ -67,7 +67,7 @@ func (client *ContainerClient) acquireLeaseCreateRequest(ctx context.Context, du
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -83,7 +83,7 @@ func (client *ContainerClient) acquireLeaseCreateRequest(ctx context.Context, du
if options != nil && options.ProposedLeaseID != nil {
req.Raw().Header["x-ms-proposed-lease-id"] = []string{*options.ProposedLeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -126,7 +126,7 @@ func (client *ContainerClient) acquireLeaseHandleResponse(resp *http.Response) (
// to 60 seconds, or can be infinite
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientBreakLeaseOptions contains the optional parameters for the ContainerClient.BreakLease method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
func (client *ContainerClient) BreakLease(ctx context.Context, options *ContainerClientBreakLeaseOptions, modifiedAccessConditions *ModifiedAccessConditions) (ContainerClientBreakLeaseResponse, error) {
@ -159,7 +159,7 @@ func (client *ContainerClient) breakLeaseCreateRequest(ctx context.Context, opti
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -174,7 +174,7 @@ func (client *ContainerClient) breakLeaseCreateRequest(ctx context.Context, opti
if options != nil && options.BreakPeriod != nil {
req.Raw().Header["x-ms-lease-break-period"] = []string{strconv.FormatInt(int64(*options.BreakPeriod), 10)}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -222,7 +222,7 @@ func (client *ContainerClient) breakLeaseHandleResponse(resp *http.Response) (Co
// to 60 seconds, or can be infinite
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - leaseID - Specifies the current lease ID on the resource.
// - proposedLeaseID - Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request) if the proposed
// lease ID is not in the correct format. See Guid Constructor (String) for a list of valid GUID
@ -259,7 +259,7 @@ func (client *ContainerClient) changeLeaseCreateRequest(ctx context.Context, lea
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -273,7 +273,7 @@ func (client *ContainerClient) changeLeaseCreateRequest(ctx context.Context, lea
req.Raw().Header["x-ms-lease-action"] = []string{"change"}
req.Raw().Header["x-ms-lease-id"] = []string{leaseID}
req.Raw().Header["x-ms-proposed-lease-id"] = []string{proposedLeaseID}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -316,7 +316,7 @@ func (client *ContainerClient) changeLeaseHandleResponse(resp *http.Response) (C
// fails
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientCreateOptions contains the optional parameters for the ContainerClient.Create method.
// - ContainerCPKScopeInfo - ContainerCPKScopeInfo contains a group of parameters for the ContainerClient.Create method.
func (client *ContainerClient) Create(ctx context.Context, options *ContainerClientCreateOptions, containerCPKScopeInfo *ContainerCPKScopeInfo) (ContainerClientCreateResponse, error) {
@ -348,7 +348,7 @@ func (client *ContainerClient) createCreateRequest(ctx context.Context, options
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.Access != nil {
req.Raw().Header["x-ms-blob-public-access"] = []string{string(*options.Access)}
@ -369,7 +369,7 @@ func (client *ContainerClient) createCreateRequest(ctx context.Context, options
}
}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -409,7 +409,7 @@ func (client *ContainerClient) createHandleResponse(resp *http.Response) (Contai
// deleted during garbage collection
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientDeleteOptions contains the optional parameters for the ContainerClient.Delete method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -442,7 +442,7 @@ func (client *ContainerClient) deleteCreateRequest(ctx context.Context, options
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -456,7 +456,7 @@ func (client *ContainerClient) deleteCreateRequest(ctx context.Context, options
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -486,7 +486,7 @@ func (client *ContainerClient) deleteHandleResponse(resp *http.Response) (Contai
// Filter blobs searches within the given container.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - where - Filters the results to return only to return only blobs whose tags match the specified expression.
// - options - ContainerClientFilterBlobsOptions contains the optional parameters for the ContainerClient.FilterBlobs method.
func (client *ContainerClient) FilterBlobs(ctx context.Context, where string, options *ContainerClientFilterBlobsOptions) (ContainerClientFilterBlobsResponse, error) {
@ -529,12 +529,12 @@ func (client *ContainerClient) filterBlobsCreateRequest(ctx context.Context, whe
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
reqQP.Set("where", where)
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -567,7 +567,7 @@ func (client *ContainerClient) filterBlobsHandleResponse(resp *http.Response) (C
// be accessed publicly.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientGetAccessPolicyOptions contains the optional parameters for the ContainerClient.GetAccessPolicy
// method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -601,7 +601,7 @@ func (client *ContainerClient) getAccessPolicyCreateRequest(ctx context.Context,
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
@ -609,7 +609,7 @@ func (client *ContainerClient) getAccessPolicyCreateRequest(ctx context.Context,
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -654,7 +654,7 @@ func (client *ContainerClient) getAccessPolicyHandleResponse(resp *http.Response
// GetAccountInfo - Returns the sku name and account kind
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientGetAccountInfoOptions contains the optional parameters for the ContainerClient.GetAccountInfo
// method.
func (client *ContainerClient) GetAccountInfo(ctx context.Context, options *ContainerClientGetAccountInfoOptions) (ContainerClientGetAccountInfoResponse, error) {
@ -687,12 +687,12 @@ func (client *ContainerClient) getAccountInfoCreateRequest(ctx context.Context,
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -735,7 +735,7 @@ func (client *ContainerClient) getAccountInfoHandleResponse(resp *http.Response)
// does not include the container's list of blobs
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientGetPropertiesOptions contains the optional parameters for the ContainerClient.GetProperties method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
func (client *ContainerClient) GetProperties(ctx context.Context, options *ContainerClientGetPropertiesOptions, leaseAccessConditions *LeaseAccessConditions) (ContainerClientGetPropertiesResponse, error) {
@ -767,7 +767,7 @@ func (client *ContainerClient) getPropertiesCreateRequest(ctx context.Context, o
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
@ -775,7 +775,7 @@ func (client *ContainerClient) getPropertiesCreateRequest(ctx context.Context, o
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -864,7 +864,7 @@ func (client *ContainerClient) getPropertiesHandleResponse(resp *http.Response)
// NewListBlobFlatSegmentPager - [Update] The List Blobs operation returns a list of the blobs under the specified container
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientListBlobFlatSegmentOptions contains the optional parameters for the ContainerClient.NewListBlobFlatSegmentPager
// method.
//
@ -892,12 +892,12 @@ func (client *ContainerClient) ListBlobFlatSegmentCreateRequest(ctx context.Cont
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -931,7 +931,7 @@ func (client *ContainerClient) ListBlobFlatSegmentHandleResponse(resp *http.Resp
// NewListBlobHierarchySegmentPager - [Update] The List Blobs operation returns a list of the blobs under the specified container
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - delimiter - When the request includes this parameter, the operation returns a BlobPrefix element in the response body that
// acts as a placeholder for all blobs whose names begin with the same substring up to the
// appearance of the delimiter character. The delimiter may be a single character or a string.
@ -983,12 +983,12 @@ func (client *ContainerClient) ListBlobHierarchySegmentCreateRequest(ctx context
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1024,7 +1024,7 @@ func (client *ContainerClient) ListBlobHierarchySegmentHandleResponse(resp *http
// to 60 seconds, or can be infinite
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - leaseID - Specifies the current lease ID on the resource.
// - options - ContainerClientReleaseLeaseOptions contains the optional parameters for the ContainerClient.ReleaseLease method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -1058,7 +1058,7 @@ func (client *ContainerClient) releaseLeaseCreateRequest(ctx context.Context, le
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -1071,7 +1071,7 @@ func (client *ContainerClient) releaseLeaseCreateRequest(ctx context.Context, le
}
req.Raw().Header["x-ms-lease-action"] = []string{"release"}
req.Raw().Header["x-ms-lease-id"] = []string{leaseID}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1110,7 +1110,7 @@ func (client *ContainerClient) releaseLeaseHandleResponse(resp *http.Response) (
// Rename - Renames an existing container.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - sourceContainerName - Required. Specifies the name of the container to rename.
// - options - ContainerClientRenameOptions contains the optional parameters for the ContainerClient.Rename method.
func (client *ContainerClient) Rename(ctx context.Context, sourceContainerName string, options *ContainerClientRenameOptions) (ContainerClientRenameResponse, error) {
@ -1143,7 +1143,7 @@ func (client *ContainerClient) renameCreateRequest(ctx context.Context, sourceCo
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
@ -1152,7 +1152,7 @@ func (client *ContainerClient) renameCreateRequest(ctx context.Context, sourceCo
if options != nil && options.SourceLeaseID != nil {
req.Raw().Header["x-ms-source-lease-id"] = []string{*options.SourceLeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1182,7 +1182,7 @@ func (client *ContainerClient) renameHandleResponse(resp *http.Response) (Contai
// to 60 seconds, or can be infinite
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - leaseID - Specifies the current lease ID on the resource.
// - options - ContainerClientRenewLeaseOptions contains the optional parameters for the ContainerClient.RenewLease method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -1216,7 +1216,7 @@ func (client *ContainerClient) renewLeaseCreateRequest(ctx context.Context, leas
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -1229,7 +1229,7 @@ func (client *ContainerClient) renewLeaseCreateRequest(ctx context.Context, leas
}
req.Raw().Header["x-ms-lease-action"] = []string{"renew"}
req.Raw().Header["x-ms-lease-id"] = []string{leaseID}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1271,7 +1271,7 @@ func (client *ContainerClient) renewLeaseHandleResponse(resp *http.Response) (Co
// Restore - Restores a previously-deleted container.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientRestoreOptions contains the optional parameters for the ContainerClient.Restore method.
func (client *ContainerClient) Restore(ctx context.Context, options *ContainerClientRestoreOptions) (ContainerClientRestoreResponse, error) {
var err error
@ -1303,7 +1303,7 @@ func (client *ContainerClient) restoreCreateRequest(ctx context.Context, options
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
@ -1314,7 +1314,7 @@ func (client *ContainerClient) restoreCreateRequest(ctx context.Context, options
if options != nil && options.DeletedContainerVersion != nil {
req.Raw().Header["x-ms-deleted-container-version"] = []string{*options.DeletedContainerVersion}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1344,7 +1344,7 @@ func (client *ContainerClient) restoreHandleResponse(resp *http.Response) (Conta
// may be accessed publicly.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - containerACL - the acls for the container
// - options - ContainerClientSetAccessPolicyOptions contains the optional parameters for the ContainerClient.SetAccessPolicy
// method.
@ -1380,7 +1380,7 @@ func (client *ContainerClient) setAccessPolicyCreateRequest(ctx context.Context,
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -1397,7 +1397,7 @@ func (client *ContainerClient) setAccessPolicyCreateRequest(ctx context.Context,
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
type wrapper struct {
XMLName xml.Name `xml:"SignedIdentifiers"`
ContainerACL *[]*SignedIdentifier `xml:"SignedIdentifier"`
@ -1443,7 +1443,7 @@ func (client *ContainerClient) setAccessPolicyHandleResponse(resp *http.Response
// SetMetadata - operation sets one or more user-defined name-value pairs for the specified container.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ContainerClientSetMetadataOptions contains the optional parameters for the ContainerClient.SetMetadata method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
// - ModifiedAccessConditions - ModifiedAccessConditions contains a group of parameters for the ContainerClient.Delete method.
@ -1477,7 +1477,7 @@ func (client *ContainerClient) setMetadataCreateRequest(ctx context.Context, opt
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if modifiedAccessConditions != nil && modifiedAccessConditions.IfModifiedSince != nil {
req.Raw().Header["If-Modified-Since"] = []string{(*modifiedAccessConditions.IfModifiedSince).In(gmt).Format(time.RFC1123)}
@ -1495,7 +1495,7 @@ func (client *ContainerClient) setMetadataCreateRequest(ctx context.Context, opt
}
}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -1534,7 +1534,7 @@ func (client *ContainerClient) setMetadataHandleResponse(resp *http.Response) (C
// SubmitBatch - The Batch operation allows multiple API calls to be embedded into a single HTTP request.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - multipartContentType - Required. The value of this header must be multipart/mixed with a batch boundary. Example header
// value: multipart/mixed; boundary=batch_
@ -1570,7 +1570,7 @@ func (client *ContainerClient) submitBatchCreateRequest(ctx context.Context, con
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
runtime.SkipBodyDownload(req)
req.Raw().Header["Accept"] = []string{"application/xml"}
req.Raw().Header["Content-Length"] = []string{strconv.FormatInt(contentLength, 10)}
@ -1578,7 +1578,7 @@ func (client *ContainerClient) submitBatchCreateRequest(ctx context.Context, con
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := req.SetBody(body, multipartContentType); err != nil {
return nil, err
}

View file

@ -86,6 +86,9 @@ type BlobName struct {
type BlobPrefix struct {
// REQUIRED
Name *string `xml:"Name"`
// Properties of a blob
Properties *BlobProperties `xml:"Properties"`
}
// BlobProperties - Properties of a blob

View file

@ -43,6 +43,13 @@ type AppendBlobClientAppendBlockOptions struct {
// analytics logging is enabled.
RequestID *string
// Required if the request body is a structured message. Specifies the message schema version and properties.
StructuredBodyType *string
// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message
// body. Will always be smaller than Content-Length.
StructuredContentLength *int64
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32
@ -236,9 +243,18 @@ type BlobClientDeleteImmutabilityPolicyOptions struct {
// analytics logging is enabled.
RequestID *string
// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more
// information on working with blob snapshots, see Creating a Snapshot of a Blob.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob]
Snapshot *string
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32
// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on.
// It's for service version 2019-10-10 and newer.
VersionID *string
}
// BlobClientDeleteOptions contains the optional parameters for the BlobClient.Delete method.
@ -291,6 +307,10 @@ type BlobClientDownloadOptions struct {
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob]
Snapshot *string
// Specifies the response content should be returned as a structured message and specifies the message schema version and
// properties.
StructuredBodyType *string
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32
@ -429,9 +449,18 @@ type BlobClientSetImmutabilityPolicyOptions struct {
// analytics logging is enabled.
RequestID *string
// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more
// information on working with blob snapshots, see Creating a Snapshot of a Blob.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob]
Snapshot *string
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32
// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on.
// It's for service version 2019-10-10 and newer.
VersionID *string
}
// BlobClientSetLegalHoldOptions contains the optional parameters for the BlobClient.SetLegalHold method.
@ -440,9 +469,18 @@ type BlobClientSetLegalHoldOptions struct {
// analytics logging is enabled.
RequestID *string
// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more
// information on working with blob snapshots, see Creating a Snapshot of a Blob.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob]
Snapshot *string
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32
// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on.
// It's for service version 2019-10-10 and newer.
VersionID *string
}
// BlobClientSetMetadataOptions contains the optional parameters for the BlobClient.SetMetadata method.
@ -708,6 +746,13 @@ type BlockBlobClientStageBlockOptions struct {
// analytics logging is enabled.
RequestID *string
// Required if the request body is a structured message. Specifies the message schema version and properties.
StructuredBodyType *string
// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message
// body. Will always be smaller than Content-Length.
StructuredContentLength *int64
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32
@ -745,6 +790,13 @@ type BlockBlobClientUploadOptions struct {
// analytics logging is enabled.
RequestID *string
// Required if the request body is a structured message. Specifies the message schema version and properties.
StructuredBodyType *string
// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message
// body. Will always be smaller than Content-Length.
StructuredContentLength *int64
// Optional. Indicates the tier to be set on the blob.
Tier *AccessTier
@ -1316,6 +1368,13 @@ type PageBlobClientUploadPagesOptions struct {
// analytics logging is enabled.
RequestID *string
// Required if the request body is a structured message. Specifies the message schema version and properties.
StructuredBodyType *string
// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message
// body. Will always be smaller than Content-Length.
StructuredContentLength *int64
// The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations.
// [https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations]
Timeout *int32

View file

@ -27,7 +27,7 @@ type PageBlobClient struct {
// ClearPages - The Clear Pages operation clears a set of pages from a page blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - options - PageBlobClientClearPagesOptions contains the optional parameters for the PageBlobClient.ClearPages method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -114,7 +114,7 @@ func (client *PageBlobClient) clearPagesCreateRequest(ctx context.Context, conte
if options != nil && options.Range != nil {
req.Raw().Header["x-ms-range"] = []string{*options.Range}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -178,7 +178,7 @@ func (client *PageBlobClient) clearPagesHandleResponse(resp *http.Response) (Pag
// 2016-05-31.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - copySource - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that specifies
// a page blob snapshot. The value should be URL-encoded as it would appear in a request
// URI. The source blob must either be public or must be authenticated via a shared access signature.
@ -235,7 +235,7 @@ func (client *PageBlobClient) copyIncrementalCreateRequest(ctx context.Context,
if modifiedAccessConditions != nil && modifiedAccessConditions.IfTags != nil {
req.Raw().Header["x-ms-if-tags"] = []string{*modifiedAccessConditions.IfTags}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -280,7 +280,7 @@ func (client *PageBlobClient) copyIncrementalHandleResponse(resp *http.Response)
// Create - The Create operation creates a new page blob.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - blobContentLength - This header specifies the maximum size for the page blob, up to 1 TB. The page blob size must be aligned
// to a 512-byte boundary.
@ -399,7 +399,7 @@ func (client *PageBlobClient) createCreateRequest(ctx context.Context, contentLe
if options != nil && options.BlobTagsString != nil {
req.Raw().Header["x-ms-tags"] = []string{*options.BlobTagsString}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -461,7 +461,7 @@ func (client *PageBlobClient) createHandleResponse(resp *http.Response) (PageBlo
// NewGetPageRangesPager - The Get Page Ranges operation returns the list of valid page ranges for a page blob or snapshot
// of a page blob
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - PageBlobClientGetPageRangesOptions contains the optional parameters for the PageBlobClient.NewGetPageRangesPager
// method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -533,7 +533,7 @@ func (client *PageBlobClient) GetPageRangesCreateRequest(ctx context.Context, op
if options != nil && options.Range != nil {
req.Raw().Header["x-ms-range"] = []string{*options.Range}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -582,7 +582,7 @@ func (client *PageBlobClient) GetPageRangesHandleResponse(resp *http.Response) (
// NewGetPageRangesDiffPager - The Get Page Ranges Diff operation returns the list of valid page ranges for a page blob that
// were changed between target blob and previous snapshot.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - PageBlobClientGetPageRangesDiffOptions contains the optional parameters for the PageBlobClient.NewGetPageRangesDiffPager
// method.
// - LeaseAccessConditions - LeaseAccessConditions contains a group of parameters for the ContainerClient.GetProperties method.
@ -660,7 +660,7 @@ func (client *PageBlobClient) GetPageRangesDiffCreateRequest(ctx context.Context
if options != nil && options.Range != nil {
req.Raw().Header["x-ms-range"] = []string{*options.Range}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -709,7 +709,7 @@ func (client *PageBlobClient) GetPageRangesDiffHandleResponse(resp *http.Respons
// Resize - Resize the Blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - blobContentLength - This header specifies the maximum size for the page blob, up to 1 TB. The page blob size must be aligned
// to a 512-byte boundary.
// - options - PageBlobClientResizeOptions contains the optional parameters for the PageBlobClient.Resize method.
@ -782,7 +782,7 @@ func (client *PageBlobClient) resizeCreateRequest(ctx context.Context, blobConte
if leaseAccessConditions != nil && leaseAccessConditions.LeaseID != nil {
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -828,7 +828,7 @@ func (client *PageBlobClient) resizeHandleResponse(resp *http.Response) (PageBlo
// UpdateSequenceNumber - Update the sequence number of the blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - sequenceNumberAction - Required if the x-ms-blob-sequence-number header is set for the request. This property applies to
// page blobs only. This property indicates how the service should modify the blob's sequence number
// - options - PageBlobClientUpdateSequenceNumberOptions contains the optional parameters for the PageBlobClient.UpdateSequenceNumber
@ -891,7 +891,7 @@ func (client *PageBlobClient) updateSequenceNumberCreateRequest(ctx context.Cont
req.Raw().Header["x-ms-lease-id"] = []string{*leaseAccessConditions.LeaseID}
}
req.Raw().Header["x-ms-sequence-number-action"] = []string{string(sequenceNumberAction)}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -937,7 +937,7 @@ func (client *PageBlobClient) updateSequenceNumberHandleResponse(resp *http.Resp
// UploadPages - The Upload Pages operation writes a range of pages to a page blob
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - body - Initial data
// - options - PageBlobClientUploadPagesOptions contains the optional parameters for the PageBlobClient.UploadPages method.
@ -1031,7 +1031,13 @@ func (client *PageBlobClient) uploadPagesCreateRequest(ctx context.Context, cont
if options != nil && options.Range != nil {
req.Raw().Header["x-ms-range"] = []string{*options.Range}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
if options != nil && options.StructuredBodyType != nil {
req.Raw().Header["x-ms-structured-body"] = []string{*options.StructuredBodyType}
}
if options != nil && options.StructuredContentLength != nil {
req.Raw().Header["x-ms-structured-content-length"] = []string{strconv.FormatInt(*options.StructuredContentLength, 10)}
}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := req.SetBody(body, "application/octet-stream"); err != nil {
return nil, err
}
@ -1098,6 +1104,9 @@ func (client *PageBlobClient) uploadPagesHandleResponse(resp *http.Response) (Pa
if val := resp.Header.Get("x-ms-request-id"); val != "" {
result.RequestID = &val
}
if val := resp.Header.Get("x-ms-structured-body"); val != "" {
result.StructuredBodyType = &val
}
if val := resp.Header.Get("x-ms-version"); val != "" {
result.Version = &val
}
@ -1108,7 +1117,7 @@ func (client *PageBlobClient) uploadPagesHandleResponse(resp *http.Response) (Pa
// a URL
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - sourceURL - Specify a URL to the copy source.
// - sourceRange - Bytes of source data in the specified range. The length of this range should match the ContentLength header
// and x-ms-range/Range destination range header.
@ -1224,7 +1233,7 @@ func (client *PageBlobClient) uploadPagesFromURLCreateRequest(ctx context.Contex
req.Raw().Header["x-ms-source-if-unmodified-since"] = []string{(*sourceModifiedAccessConditions.SourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123)}
}
req.Raw().Header["x-ms-source-range"] = []string{sourceRange}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}

View file

@ -88,6 +88,9 @@ type AppendBlobClientAppendBlockResponse struct {
// RequestID contains the information returned from the x-ms-request-id header response.
RequestID *string
// StructuredBodyType contains the information returned from the x-ms-structured-body header response.
StructuredBodyType *string
// Version contains the information returned from the x-ms-version header response.
Version *string
}
@ -469,6 +472,12 @@ type BlobClientDownloadResponse struct {
// RequestID contains the information returned from the x-ms-request-id header response.
RequestID *string
// StructuredBodyType contains the information returned from the x-ms-structured-body header response.
StructuredBodyType *string
// StructuredContentLength contains the information returned from the x-ms-structured-content-length header response.
StructuredContentLength *int64
// TagCount contains the information returned from the x-ms-tag-count header response.
TagCount *int64
@ -1170,6 +1179,9 @@ type BlockBlobClientStageBlockResponse struct {
// RequestID contains the information returned from the x-ms-request-id header response.
RequestID *string
// StructuredBodyType contains the information returned from the x-ms-structured-body header response.
StructuredBodyType *string
// Version contains the information returned from the x-ms-version header response.
Version *string
}
@ -1206,6 +1218,9 @@ type BlockBlobClientUploadResponse struct {
// RequestID contains the information returned from the x-ms-request-id header response.
RequestID *string
// StructuredBodyType contains the information returned from the x-ms-structured-body header response.
StructuredBodyType *string
// Version contains the information returned from the x-ms-version header response.
Version *string
@ -1882,6 +1897,9 @@ type PageBlobClientUploadPagesResponse struct {
// RequestID contains the information returned from the x-ms-request-id header response.
RequestID *string
// StructuredBodyType contains the information returned from the x-ms-structured-body header response.
StructuredBodyType *string
// Version contains the information returned from the x-ms-version header response.
Version *string
}

View file

@ -30,7 +30,7 @@ type ServiceClient struct {
// be scoped within the expression to a single container.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - where - Filters the results to return only to return only blobs whose tags match the specified expression.
// - options - ServiceClientFilterBlobsOptions contains the optional parameters for the ServiceClient.FilterBlobs method.
func (client *ServiceClient) FilterBlobs(ctx context.Context, where string, options *ServiceClientFilterBlobsOptions) (ServiceClientFilterBlobsResponse, error) {
@ -77,7 +77,7 @@ func (client *ServiceClient) filterBlobsCreateRequest(ctx context.Context, where
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -109,7 +109,7 @@ func (client *ServiceClient) filterBlobsHandleResponse(resp *http.Response) (Ser
// GetAccountInfo - Returns the sku name and account kind
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ServiceClientGetAccountInfoOptions contains the optional parameters for the ServiceClient.GetAccountInfo method.
func (client *ServiceClient) GetAccountInfo(ctx context.Context, options *ServiceClientGetAccountInfoOptions) (ServiceClientGetAccountInfoResponse, error) {
var err error
@ -141,12 +141,12 @@ func (client *ServiceClient) getAccountInfoCreateRequest(ctx context.Context, op
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -189,7 +189,7 @@ func (client *ServiceClient) getAccountInfoHandleResponse(resp *http.Response) (
// CORS (Cross-Origin Resource Sharing) rules.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ServiceClientGetPropertiesOptions contains the optional parameters for the ServiceClient.GetProperties method.
func (client *ServiceClient) GetProperties(ctx context.Context, options *ServiceClientGetPropertiesOptions) (ServiceClientGetPropertiesResponse, error) {
var err error
@ -221,12 +221,12 @@ func (client *ServiceClient) getPropertiesCreateRequest(ctx context.Context, opt
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -252,7 +252,7 @@ func (client *ServiceClient) getPropertiesHandleResponse(resp *http.Response) (S
// location endpoint when read-access geo-redundant replication is enabled for the storage account.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ServiceClientGetStatisticsOptions contains the optional parameters for the ServiceClient.GetStatistics method.
func (client *ServiceClient) GetStatistics(ctx context.Context, options *ServiceClientGetStatisticsOptions) (ServiceClientGetStatisticsResponse, error) {
var err error
@ -284,12 +284,12 @@ func (client *ServiceClient) getStatisticsCreateRequest(ctx context.Context, opt
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -322,7 +322,7 @@ func (client *ServiceClient) getStatisticsHandleResponse(resp *http.Response) (S
// bearer token authentication.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - keyInfo - Key information
// - options - ServiceClientGetUserDelegationKeyOptions contains the optional parameters for the ServiceClient.GetUserDelegationKey
// method.
@ -356,12 +356,12 @@ func (client *ServiceClient) getUserDelegationKeyCreateRequest(ctx context.Conte
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := runtime.MarshalAsXML(req, keyInfo); err != nil {
return nil, err
}
@ -396,7 +396,7 @@ func (client *ServiceClient) getUserDelegationKeyHandleResponse(resp *http.Respo
// NewListContainersSegmentPager - The List Containers Segment operation returns a list of the containers under the specified
// account
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - options - ServiceClientListContainersSegmentOptions contains the optional parameters for the ServiceClient.NewListContainersSegmentPager
// method.
//
@ -423,12 +423,12 @@ func (client *ServiceClient) ListContainersSegmentCreateRequest(ctx context.Cont
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
return req, nil
}
@ -454,7 +454,7 @@ func (client *ServiceClient) ListContainersSegmentHandleResponse(resp *http.Resp
// and CORS (Cross-Origin Resource Sharing) rules
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - storageServiceProperties - The StorageService properties.
// - options - ServiceClientSetPropertiesOptions contains the optional parameters for the ServiceClient.SetProperties method.
func (client *ServiceClient) SetProperties(ctx context.Context, storageServiceProperties StorageServiceProperties, options *ServiceClientSetPropertiesOptions) (ServiceClientSetPropertiesResponse, error) {
@ -487,12 +487,12 @@ func (client *ServiceClient) setPropertiesCreateRequest(ctx context.Context, sto
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
req.Raw().Header["Accept"] = []string{"application/xml"}
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := runtime.MarshalAsXML(req, storageServiceProperties); err != nil {
return nil, err
}
@ -517,7 +517,7 @@ func (client *ServiceClient) setPropertiesHandleResponse(resp *http.Response) (S
// SubmitBatch - The Batch operation allows multiple API calls to be embedded into a single HTTP request.
// If the operation fails it returns an *azcore.ResponseError type.
//
// Generated from API version 2024-08-04
// Generated from API version 2025-01-05
// - contentLength - The length of the request.
// - multipartContentType - Required. The value of this header must be multipart/mixed with a batch boundary. Example header
// value: multipart/mixed; boundary=batch_
@ -552,7 +552,7 @@ func (client *ServiceClient) submitBatchCreateRequest(ctx context.Context, conte
if options != nil && options.Timeout != nil {
reqQP.Set("timeout", strconv.FormatInt(int64(*options.Timeout), 10))
}
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().URL.RawQuery = strings.Replace(reqQP.Encode(), "+", "%20", -1)
runtime.SkipBodyDownload(req)
req.Raw().Header["Accept"] = []string{"application/xml"}
req.Raw().Header["Content-Length"] = []string{strconv.FormatInt(contentLength, 10)}
@ -560,7 +560,7 @@ func (client *ServiceClient) submitBatchCreateRequest(ctx context.Context, conte
if options != nil && options.RequestID != nil {
req.Raw().Header["x-ms-client-request-id"] = []string{*options.RequestID}
}
req.Raw().Header["x-ms-version"] = []string{ServiceVersion}
req.Raw().Header["x-ms-version"] = []string{"2025-01-05"}
if err := req.SetBody(body, multipartContentType); err != nil {
return nil, err
}

View file

@ -1,6 +1,6 @@
//go:build go1.18 && (linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris || aix)
//go:build go1.18 && (linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris || aix || zos)
// +build go1.18
// +build linux darwin dragonfly freebsd openbsd netbsd solaris aix
// +build linux darwin dragonfly freebsd openbsd netbsd solaris aix zos
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

View file

@ -190,7 +190,7 @@ func buildCanonicalizedResource(accountName, uri string, keyType SharedKeyType)
}
}
return string(cr.Bytes()), nil
return cr.String(), nil
}
func getCanonicalizedAccountName(accountName string) string {
@ -289,7 +289,7 @@ func buildCanonicalizedHeader(headers http.Header) string {
ch.WriteRune('\n')
}
return strings.TrimSuffix(string(ch.Bytes()), "\n")
return strings.TrimSuffix(ch.String(), "\n")
}
func createAuthorizationHeader(accountName string, accountKey []byte, canonicalizedString string, keyType SharedKeyType) string {

View file

@ -19,7 +19,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@ -318,11 +318,11 @@ func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) {
if err == nil && resp.Body != nil {
// copy the body and close it so callers don't have to
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return resp, err
}
resp.Body = ioutil.NopCloser(bytes.NewReader(b))
resp.Body = io.NopCloser(bytes.NewReader(b))
}
return resp, err
}
@ -459,12 +459,12 @@ func (pt *pollingTrackerBase) updateRawBody() error {
pt.rawBody = map[string]interface{}{}
if pt.resp.ContentLength != 0 {
defer pt.resp.Body.Close()
b, err := ioutil.ReadAll(pt.resp.Body)
b, err := io.ReadAll(pt.resp.Body)
if err != nil {
return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to read response body")
}
// put the body back so it's available to other callers
pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
pt.resp.Body = io.NopCloser(bytes.NewReader(b))
// observed in 204 responses over HTTP/2.0; the content length is -1 but body is empty
if len(b) == 0 {
return nil
@ -516,11 +516,11 @@ func (pt *pollingTrackerBase) updateErrorFromResponse() {
re := respErr{}
defer pt.resp.Body.Close()
var b []byte
if b, err = ioutil.ReadAll(pt.resp.Body); err != nil {
if b, err = io.ReadAll(pt.resp.Body); err != nil {
goto Default
}
// put the body back so it's available to other callers
pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
pt.resp.Body = io.NopCloser(bytes.NewReader(b))
if len(b) == 0 {
goto Default
}

View file

@ -20,7 +20,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"regexp"
"strconv"
@ -333,7 +333,7 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
// Copy and replace the Body in case it does not contain an error object.
// This will leave the Body available to the caller.
b, decodeErr := autorest.CopyAndDecode(encodedAs, resp.Body, &e)
resp.Body = ioutil.NopCloser(&b)
resp.Body = io.NopCloser(&b)
if decodeErr != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, decodeErr)
}

View file

@ -17,7 +17,6 @@ package azure
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
)
@ -315,7 +314,7 @@ func EnvironmentFromName(name string) (Environment, error) {
// This function is particularly useful in the Hybrid Cloud model, where one must define their own
// endpoints.
func EnvironmentFromFile(location string) (unmarshaled Environment, err error) {
fileContents, err := ioutil.ReadFile(location)
fileContents, err := os.ReadFile(location)
if err != nil {
return
}

View file

@ -3,7 +3,7 @@ package azure
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"strings"
@ -236,7 +236,7 @@ func retrieveMetadataEnvironment(endpoint string) (environment environmentMetada
return environment, err
}
defer response.Body.Close()
jsonResponse, err := ioutil.ReadAll(response.Body)
jsonResponse, err := io.ReadAll(response.Body)
if err != nil {
return environment, err
}

View file

@ -20,7 +20,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
@ -106,14 +105,14 @@ func (li LoggingInspector) WithInspection() PrepareDecorator {
defer r.Body.Close()
r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body))
r.Body = io.NopCloser(io.TeeReader(r.Body, &body))
if err := r.Write(&b); err != nil {
return nil, fmt.Errorf("Failed to write response: %v", err)
}
li.Logger.Printf(requestFormat, b.String())
r.Body = ioutil.NopCloser(&body)
r.Body = io.NopCloser(&body)
return p.Prepare(r)
})
}
@ -129,14 +128,14 @@ func (li LoggingInspector) ByInspecting() RespondDecorator {
return ResponderFunc(func(resp *http.Response) error {
var body, b bytes.Buffer
defer resp.Body.Close()
resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body))
resp.Body = io.NopCloser(io.TeeReader(resp.Body, &body))
if err := resp.Write(&b); err != nil {
return fmt.Errorf("Failed to write response: %v", err)
}
li.Logger.Printf(responseFormat, b.String())
resp.Body = ioutil.NopCloser(&body)
resp.Body = io.NopCloser(&body)
return r.Respond(resp)
})
}

View file

@ -21,7 +21,6 @@ import (
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
@ -268,7 +267,7 @@ func WithBytes(input *[]byte) PrepareDecorator {
}
r.ContentLength = int64(len(*input))
r.Body = ioutil.NopCloser(bytes.NewReader(*input))
r.Body = io.NopCloser(bytes.NewReader(*input))
}
return r, err
})
@ -296,7 +295,7 @@ func WithFormData(v url.Values) PrepareDecorator {
setHeader(r, http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost)
r.ContentLength = int64(len(s))
r.Body = ioutil.NopCloser(strings.NewReader(s))
r.Body = io.NopCloser(strings.NewReader(s))
}
return r, err
})
@ -331,7 +330,7 @@ func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDec
return r, err
}
setHeader(r, http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
r.Body = io.NopCloser(bytes.NewReader(body.Bytes()))
r.ContentLength = int64(body.Len())
return r, err
}
@ -346,11 +345,11 @@ func WithFile(f io.ReadCloser) PrepareDecorator {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err == nil {
b, err := ioutil.ReadAll(f)
b, err := io.ReadAll(f)
if err != nil {
return r, err
}
r.Body = ioutil.NopCloser(bytes.NewReader(b))
r.Body = io.NopCloser(bytes.NewReader(b))
r.ContentLength = int64(len(b))
}
return r, err
@ -396,7 +395,7 @@ func WithString(v string) PrepareDecorator {
r, err := p.Prepare(r)
if err == nil {
r.ContentLength = int64(len(v))
r.Body = ioutil.NopCloser(strings.NewReader(v))
r.Body = io.NopCloser(strings.NewReader(v))
}
return r, err
})
@ -413,7 +412,7 @@ func WithJSON(v interface{}) PrepareDecorator {
b, err := json.Marshal(v)
if err == nil {
r.ContentLength = int64(len(b))
r.Body = ioutil.NopCloser(bytes.NewReader(b))
r.Body = io.NopCloser(bytes.NewReader(b))
}
}
return r, err
@ -436,7 +435,7 @@ func WithXML(v interface{}) PrepareDecorator {
r.ContentLength = int64(len(bytesWithHeader))
setHeader(r, headerContentLength, fmt.Sprintf("%d", len(bytesWithHeader)))
r.Body = ioutil.NopCloser(bytes.NewReader(bytesWithHeader))
r.Body = io.NopCloser(bytes.NewReader(bytesWithHeader))
}
}
return r, err

View file

@ -20,7 +20,6 @@ import (
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
)
@ -111,7 +110,7 @@ func ByDiscardingBody() RespondDecorator {
return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil && resp != nil && resp.Body != nil {
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
return fmt.Errorf("Error discarding the response body: %v", err)
}
}
@ -160,7 +159,7 @@ func ByUnmarshallingBytes(v *[]byte) RespondDecorator {
return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil {
bytes, errInner := ioutil.ReadAll(resp.Body)
bytes, errInner := io.ReadAll(resp.Body)
if errInner != nil {
err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
} else {
@ -179,7 +178,7 @@ func ByUnmarshallingJSON(v interface{}) RespondDecorator {
return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil {
b, errInner := ioutil.ReadAll(resp.Body)
b, errInner := io.ReadAll(resp.Body)
// Some responses might include a BOM, remove for successful unmarshalling
b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
if errInner != nil {
@ -203,7 +202,7 @@ func ByUnmarshallingXML(v interface{}) RespondDecorator {
return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil {
b, errInner := ioutil.ReadAll(resp.Body)
b, errInner := io.ReadAll(resp.Body)
if errInner != nil {
err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
} else {
@ -232,9 +231,9 @@ func WithErrorUnlessStatusCode(codes ...int) RespondDecorator {
resp.Status)
if resp.Body != nil {
defer resp.Body.Close()
b, _ := ioutil.ReadAll(resp.Body)
b, _ := io.ReadAll(resp.Body)
derr.ServiceError = b
resp.Body = ioutil.NopCloser(bytes.NewReader(b))
resp.Body = io.NopCloser(bytes.NewReader(b))
}
err = derr
}

View file

@ -17,7 +17,6 @@ package autorest
import (
"bytes"
"io"
"io/ioutil"
"net/http"
)
@ -41,12 +40,12 @@ func (rr *RetriableRequest) prepareFromByteReader() (err error) {
return err
}
} else {
b, err = ioutil.ReadAll(rr.req.Body)
b, err = io.ReadAll(rr.req.Body)
if err != nil {
return err
}
}
rr.br = bytes.NewReader(b)
rr.req.Body = ioutil.NopCloser(rr.br)
rr.req.Body = io.NopCloser(rr.br)
return err
}

View file

@ -19,7 +19,7 @@ package autorest
import (
"bytes"
"io/ioutil"
"io"
"net/http"
)
@ -33,10 +33,10 @@ type RetriableRequest struct {
func (rr *RetriableRequest) Prepare() (err error) {
// preserve the request body; this is to support retry logic as
// the underlying transport will always close the reqeust body
if rr.req.Body != nil {
if rr.req.Body != nil && rr.req.Body != http.NoBody {
if rr.br != nil {
_, err = rr.br.Seek(0, 0 /*io.SeekStart*/)
rr.req.Body = ioutil.NopCloser(rr.br)
rr.req.Body = io.NopCloser(rr.br)
}
if err != nil {
return err

View file

@ -20,7 +20,6 @@ package autorest
import (
"bytes"
"io"
"io/ioutil"
"net/http"
)
@ -35,12 +34,12 @@ type RetriableRequest struct {
func (rr *RetriableRequest) Prepare() (err error) {
// preserve the request body; this is to support retry logic as
// the underlying transport will always close the reqeust body
if rr.req.Body != nil {
if rr.req.Body != nil && rr.req.Body != http.NoBody {
if rr.rc != nil {
rr.req.Body = rr.rc
} else if rr.br != nil {
_, err = rr.br.Seek(0, io.SeekStart)
rr.req.Body = ioutil.NopCloser(rr.br)
rr.req.Body = io.NopCloser(rr.br)
}
if err != nil {
return err

View file

@ -20,7 +20,6 @@ import (
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
@ -217,7 +216,7 @@ func IsTemporaryNetworkError(err error) bool {
// DrainResponseBody reads the response body then closes it.
func DrainResponseBody(resp *http.Response) error {
if resp != nil && resp.Body != nil {
_, err := io.Copy(ioutil.Discard, resp.Body)
_, err := io.Copy(io.Discard, resp.Body)
resp.Body.Close()
return err
}

View file

@ -18,6 +18,8 @@ import (
"encoding/pem"
"errors"
"fmt"
"os"
"strings"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base"
@ -315,16 +317,21 @@ func New(authority, clientID string, cred Credential, options ...Option) (Client
if err != nil {
return Client{}, err
}
autoEnabledRegion := os.Getenv("MSAL_FORCE_REGION")
opts := clientOptions{
authority: authority,
// if the caller specified a token provider, it will handle all details of authentication, using Client only as a token cache
disableInstanceDiscovery: cred.tokenProvider != nil,
httpClient: shared.DefaultClient,
azureRegion: autoEnabledRegion,
}
for _, o := range options {
o(&opts)
}
if strings.EqualFold(opts.azureRegion, "DisableMsalForceRegion") {
opts.azureRegion = ""
}
baseOpts := []base.Option{
base.WithCacheAccessor(opts.accessor),
base.WithClientCapabilities(opts.capabilities),

View file

@ -89,8 +89,23 @@ type AuthResult struct {
ExpiresOn time.Time
GrantedScopes []string
DeclinedScopes []string
Metadata AuthResultMetadata
}
// AuthResultMetadata which contains meta data for the AuthResult
type AuthResultMetadata struct {
TokenSource TokenSource
}
type TokenSource int
// These are all the types of token flows.
const (
SourceUnknown TokenSource = 0
IdentityProvider TokenSource = 1
Cache TokenSource = 2
)
// AuthResultFromStorage creates an AuthResult from a storage token response (which is generated from the cache).
func AuthResultFromStorage(storageTokenResponse storage.TokenResponse) (AuthResult, error) {
if err := storageTokenResponse.AccessToken.Validate(); err != nil {
@ -109,7 +124,17 @@ func AuthResultFromStorage(storageTokenResponse storage.TokenResponse) (AuthResu
return AuthResult{}, fmt.Errorf("problem decoding JWT token: %w", err)
}
}
return AuthResult{account, idToken, accessToken, storageTokenResponse.AccessToken.ExpiresOn.T, grantedScopes, nil}, nil
return AuthResult{
Account: account,
IDToken: idToken,
AccessToken: accessToken,
ExpiresOn: storageTokenResponse.AccessToken.ExpiresOn.T,
GrantedScopes: grantedScopes,
DeclinedScopes: nil,
Metadata: AuthResultMetadata{
TokenSource: Cache,
},
}, nil
}
// NewAuthResult creates an AuthResult.
@ -123,6 +148,9 @@ func NewAuthResult(tokenResponse accesstokens.TokenResponse, account shared.Acco
AccessToken: tokenResponse.AccessToken,
ExpiresOn: tokenResponse.ExpiresOn.T,
GrantedScopes: tokenResponse.GrantedScopes.Slice,
Metadata: AuthResultMetadata{
TokenSource: IdentityProvider,
},
}, nil
}

View file

@ -18,10 +18,6 @@ import (
)
const addField = "AdditionalFields"
const (
marshalJSON = "MarshalJSON"
unmarshalJSON = "UnmarshalJSON"
)
var (
leftBrace = []byte("{")[0]
@ -106,48 +102,38 @@ func delimIs(got json.Token, want rune) bool {
// hasMarshalJSON will determine if the value or a pointer to this value has
// the MarshalJSON method.
func hasMarshalJSON(v reflect.Value) bool {
if method := v.MethodByName(marshalJSON); method.Kind() != reflect.Invalid {
_, ok := v.Interface().(json.Marshaler)
return ok
}
if v.Kind() == reflect.Ptr {
v = v.Elem()
} else {
if !v.CanAddr() {
return false
ok := false
if _, ok = v.Interface().(json.Marshaler); !ok {
var i any
if v.Kind() == reflect.Ptr {
i = v.Elem().Interface()
} else if v.CanAddr() {
i = v.Addr().Interface()
}
v = v.Addr()
_, ok = i.(json.Marshaler)
}
if method := v.MethodByName(marshalJSON); method.Kind() != reflect.Invalid {
_, ok := v.Interface().(json.Marshaler)
return ok
}
return false
return ok
}
// callMarshalJSON will call MarshalJSON() method on the value or a pointer to this value.
// This will panic if the method is not defined.
func callMarshalJSON(v reflect.Value) ([]byte, error) {
if method := v.MethodByName(marshalJSON); method.Kind() != reflect.Invalid {
marsh := v.Interface().(json.Marshaler)
if marsh, ok := v.Interface().(json.Marshaler); ok {
return marsh.MarshalJSON()
}
if v.Kind() == reflect.Ptr {
v = v.Elem()
if marsh, ok := v.Elem().Interface().(json.Marshaler); ok {
return marsh.MarshalJSON()
}
} else {
if v.CanAddr() {
v = v.Addr()
if marsh, ok := v.Addr().Interface().(json.Marshaler); ok {
return marsh.MarshalJSON()
}
}
}
if method := v.MethodByName(unmarshalJSON); method.Kind() != reflect.Invalid {
marsh := v.Interface().(json.Marshaler)
return marsh.MarshalJSON()
}
panic(fmt.Sprintf("callMarshalJSON called on type %T that does not have MarshalJSON defined", v.Interface()))
}
@ -162,12 +148,8 @@ func hasUnmarshalJSON(v reflect.Value) bool {
v = v.Addr()
}
if method := v.MethodByName(unmarshalJSON); method.Kind() != reflect.Invalid {
_, ok := v.Interface().(json.Unmarshaler)
return ok
}
return false
_, ok := v.Interface().(json.Unmarshaler)
return ok
}
// hasOmitEmpty indicates if the field has instructed us to not output

View file

@ -7,6 +7,7 @@ package local
import (
"context"
"fmt"
"html"
"net"
"net/http"
"strconv"
@ -141,7 +142,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
headerErr := q.Get("error")
if headerErr != "" {
desc := q.Get("error_description")
desc := html.EscapeString(q.Get("error_description"))
// Note: It is a little weird we handle some errors by not going to the failPage. If they all should,
// change this to s.error() and make s.error() write the failPage instead of an error code.
_, _ = w.Write([]byte(fmt.Sprintf(failPage, headerErr, desc)))

View file

@ -10,6 +10,8 @@ import (
"io"
"time"
"github.com/google/uuid"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/exported"
internalTime "github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/json/types/time"
@ -18,7 +20,6 @@ import (
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/wstrust"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/wstrust/defs"
"github.com/google/uuid"
)
// ResolveEndpointer contains the methods for resolving authority endpoints.
@ -331,7 +332,7 @@ func (t *Client) DeviceCode(ctx context.Context, authParams authority.AuthParams
func (t *Client) resolveEndpoint(ctx context.Context, authParams *authority.AuthParams, userPrincipalName string) error {
endpoints, err := t.Resolver.ResolveEndpoints(ctx, authParams.AuthorityInfo, userPrincipalName)
if err != nil {
return fmt.Errorf("unable to resolve an endpoint: %s", err)
return fmt.Errorf("unable to resolve an endpoint: %w", err)
}
authParams.Endpoints = endpoints
return nil

View file

@ -23,7 +23,7 @@ import (
const (
authorizationEndpoint = "https://%v/%v/oauth2/v2.0/authorize"
instanceDiscoveryEndpoint = "https://%v/common/discovery/instance"
aadInstanceDiscoveryEndpoint = "https://%v/common/discovery/instance"
tenantDiscoveryEndpointWithRegion = "https://%s.%s/%s/v2.0/.well-known/openid-configuration"
regionName = "REGION_NAME"
defaultAPIVersion = "2021-10-01"
@ -47,13 +47,12 @@ type jsonCaller interface {
}
var aadTrustedHostList = map[string]bool{
"login.windows.net": true, // Microsoft Azure Worldwide - Used in validation scenarios where host is not this list
"login.chinacloudapi.cn": true, // Microsoft Azure China
"login.microsoftonline.de": true, // Microsoft Azure Blackforest
"login-us.microsoftonline.com": true, // Microsoft Azure US Government - Legacy
"login.microsoftonline.us": true, // Microsoft Azure US Government
"login.microsoftonline.com": true, // Microsoft Azure Worldwide
"login.cloudgovapi.us": true, // Microsoft Azure US Government
"login.windows.net": true, // Microsoft Azure Worldwide - Used in validation scenarios where host is not this list
"login.partner.microsoftonline.cn": true, // Microsoft Azure China
"login.microsoftonline.de": true, // Microsoft Azure Blackforest
"login-us.microsoftonline.com": true, // Microsoft Azure US Government - Legacy
"login.microsoftonline.us": true, // Microsoft Azure US Government
"login.microsoftonline.com": true, // Microsoft Azure Worldwide
}
// TrustedHost checks if an AAD host is trusted/valid.
@ -137,8 +136,12 @@ const (
const (
AAD = "MSSTS"
ADFS = "ADFS"
DSTS = "DSTS"
)
// DSTSTenant is referenced throughout multiple files, let us use a const in case we ever need to change it.
const DSTSTenant = "7a433bfc-2514-4697-b467-e0933190487f"
// AuthenticationScheme is an extensibility mechanism designed to be used only by Azure Arc for proof of possession access tokens.
type AuthenticationScheme interface {
// Extra parameters that are added to the request to the /token endpoint.
@ -236,23 +239,26 @@ func NewAuthParams(clientID string, authorityInfo Info) AuthParams {
// - the client is configured to authenticate only Microsoft accounts via the "consumers" endpoint
// - the resulting authority URL is invalid
func (p AuthParams) WithTenant(ID string) (AuthParams, error) {
switch ID {
case "", p.AuthorityInfo.Tenant:
// keep the default tenant because the caller didn't override it
if ID == "" || ID == p.AuthorityInfo.Tenant {
return p, nil
case "common", "consumers", "organizations":
if p.AuthorityInfo.AuthorityType == AAD {
}
var authority string
switch p.AuthorityInfo.AuthorityType {
case AAD:
if ID == "common" || ID == "consumers" || ID == "organizations" {
return p, fmt.Errorf(`tenant ID must be a specific tenant, not "%s"`, ID)
}
// else we'll return a better error below
if p.AuthorityInfo.Tenant == "consumers" {
return p, errors.New(`client is configured to authenticate only personal Microsoft accounts, via the "consumers" endpoint`)
}
authority = "https://" + path.Join(p.AuthorityInfo.Host, ID)
case ADFS:
return p, errors.New("ADFS authority doesn't support tenants")
case DSTS:
return p, errors.New("dSTS authority doesn't support tenants")
}
if p.AuthorityInfo.AuthorityType != AAD {
return p, errors.New("the authority doesn't support tenants")
}
if p.AuthorityInfo.Tenant == "consumers" {
return p, errors.New(`client is configured to authenticate only personal Microsoft accounts, via the "consumers" endpoint`)
}
authority := "https://" + path.Join(p.AuthorityInfo.Host, ID)
info, err := NewInfoFromAuthorityURI(authority, p.AuthorityInfo.ValidateAuthority, p.AuthorityInfo.InstanceDiscoveryDisabled)
if err == nil {
info.Region = p.AuthorityInfo.Region
@ -344,44 +350,57 @@ type Info struct {
Host string
CanonicalAuthorityURI string
AuthorityType string
UserRealmURIPrefix string
ValidateAuthority bool
Tenant string
Region string
InstanceDiscoveryDisabled bool
}
func firstPathSegment(u *url.URL) (string, error) {
pathParts := strings.Split(u.EscapedPath(), "/")
if len(pathParts) >= 2 {
return pathParts[1], nil
}
return "", errors.New(`authority must be an https URL such as "https://login.microsoftonline.com/<your tenant>"`)
}
// NewInfoFromAuthorityURI creates an AuthorityInfo instance from the authority URL provided.
func NewInfoFromAuthorityURI(authority string, validateAuthority bool, instanceDiscoveryDisabled bool) (Info, error) {
u, err := url.Parse(strings.ToLower(authority))
if err != nil || u.Scheme != "https" {
return Info{}, errors.New(`authority must be an https URL such as "https://login.microsoftonline.com/<your tenant>"`)
cannonicalAuthority := authority
// suffix authority with / if it doesn't have one
if !strings.HasSuffix(cannonicalAuthority, "/") {
cannonicalAuthority += "/"
}
tenant, err := firstPathSegment(u)
u, err := url.Parse(strings.ToLower(cannonicalAuthority))
if err != nil {
return Info{}, err
return Info{}, fmt.Errorf("couldn't parse authority url: %w", err)
}
if u.Scheme != "https" {
return Info{}, errors.New("authority url scheme must be https")
}
pathParts := strings.Split(u.EscapedPath(), "/")
if len(pathParts) < 3 {
return Info{}, errors.New(`authority must be an URL such as "https://login.microsoftonline.com/<your tenant>"`)
}
authorityType := AAD
if tenant == "adfs" {
tenant := pathParts[1]
switch tenant {
case "adfs":
authorityType = ADFS
case "dstsv2":
if len(pathParts) != 4 {
return Info{}, fmt.Errorf("dSTS authority must be an https URL such as https://<authority>/dstsv2/%s", DSTSTenant)
}
if pathParts[2] != DSTSTenant {
return Info{}, fmt.Errorf("dSTS authority only accepts a single tenant %q", DSTSTenant)
}
authorityType = DSTS
tenant = DSTSTenant
}
// u.Host includes the port, if any, which is required for private cloud deployments
return Info{
Host: u.Host,
CanonicalAuthorityURI: fmt.Sprintf("https://%v/%v/", u.Host, tenant),
CanonicalAuthorityURI: cannonicalAuthority,
AuthorityType: authorityType,
UserRealmURIPrefix: fmt.Sprintf("https://%v/common/userrealm/", u.Hostname()),
ValidateAuthority: validateAuthority,
Tenant: tenant,
InstanceDiscoveryDisabled: instanceDiscoveryDisabled,
@ -525,7 +544,7 @@ func (c Client) AADInstanceDiscovery(ctx context.Context, authorityInfo Info) (I
discoveryHost = authorityInfo.Host
}
endpoint := fmt.Sprintf(instanceDiscoveryEndpoint, discoveryHost)
endpoint := fmt.Sprintf(aadInstanceDiscoveryEndpoint, discoveryHost)
err = c.Comm.JSONCall(ctx, endpoint, http.Header{}, qv, nil, &resp)
}
return resp, err
@ -543,17 +562,19 @@ func detectRegion(ctx context.Context) string {
client := http.Client{
Timeout: time.Duration(2 * time.Second),
}
req, _ := http.NewRequest("GET", imdsEndpoint, nil)
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, imdsEndpoint, nil)
req.Header.Set("Metadata", "true")
resp, err := client.Do(req)
if err == nil {
defer resp.Body.Close()
}
// If the request times out or there is an error, it is retried once
if err != nil || resp.StatusCode != 200 {
if err != nil || resp.StatusCode != http.StatusOK {
resp, err = client.Do(req)
if err != nil || resp.StatusCode != 200 {
if err != nil || resp.StatusCode != http.StatusOK {
return ""
}
}
defer resp.Body.Close()
response, err := io.ReadAll(resp.Body)
if err != nil {
return ""

View file

@ -18,10 +18,11 @@ import (
"strings"
"time"
"github.com/google/uuid"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
customJSON "github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/json"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/version"
"github.com/google/uuid"
)
// HTTPClient represents an HTTP client.
@ -70,15 +71,13 @@ func (c *Client) JSONCall(ctx context.Context, endpoint string, headers http.Hea
unmarshal = customJSON.Unmarshal
}
u, err := url.Parse(endpoint)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s?%s", endpoint, qv.Encode()), nil)
if err != nil {
return fmt.Errorf("could not parse path URL(%s): %w", endpoint, err)
return fmt.Errorf("could not create request: %w", err)
}
u.RawQuery = qv.Encode()
addStdHeaders(headers)
req := &http.Request{Method: http.MethodGet, URL: u, Header: headers}
req.Header = headers
if body != nil {
// Note: In case your wondering why we are not gzip encoding....

View file

@ -18,9 +18,6 @@ import (
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
)
// ADFS is an active directory federation service authority type.
const ADFS = "ADFS"
type cacheEntry struct {
Endpoints authority.Endpoints
ValidForDomainsInList map[string]bool
@ -51,7 +48,7 @@ func (m *authorityEndpoint) ResolveEndpoints(ctx context.Context, authorityInfo
return endpoints, nil
}
endpoint, err := m.openIDConfigurationEndpoint(ctx, authorityInfo, userPrincipalName)
endpoint, err := m.openIDConfigurationEndpoint(ctx, authorityInfo)
if err != nil {
return authority.Endpoints{}, err
}
@ -83,7 +80,7 @@ func (m *authorityEndpoint) cachedEndpoints(authorityInfo authority.Info, userPr
defer m.mu.Unlock()
if cacheEntry, ok := m.cache[authorityInfo.CanonicalAuthorityURI]; ok {
if authorityInfo.AuthorityType == ADFS {
if authorityInfo.AuthorityType == authority.ADFS {
domain, err := adfsDomainFromUpn(userPrincipalName)
if err == nil {
if _, ok := cacheEntry.ValidForDomainsInList[domain]; ok {
@ -102,7 +99,7 @@ func (m *authorityEndpoint) addCachedEndpoints(authorityInfo authority.Info, use
updatedCacheEntry := createcacheEntry(endpoints)
if authorityInfo.AuthorityType == ADFS {
if authorityInfo.AuthorityType == authority.ADFS {
// Since we're here, we've made a call to the backend. We want to ensure we're caching
// the latest values from the server.
if cacheEntry, ok := m.cache[authorityInfo.CanonicalAuthorityURI]; ok {
@ -119,9 +116,12 @@ func (m *authorityEndpoint) addCachedEndpoints(authorityInfo authority.Info, use
m.cache[authorityInfo.CanonicalAuthorityURI] = updatedCacheEntry
}
func (m *authorityEndpoint) openIDConfigurationEndpoint(ctx context.Context, authorityInfo authority.Info, userPrincipalName string) (string, error) {
if authorityInfo.Tenant == "adfs" {
func (m *authorityEndpoint) openIDConfigurationEndpoint(ctx context.Context, authorityInfo authority.Info) (string, error) {
if authorityInfo.AuthorityType == authority.ADFS {
return fmt.Sprintf("https://%s/adfs/.well-known/openid-configuration", authorityInfo.Host), nil
} else if authorityInfo.AuthorityType == authority.DSTS {
return fmt.Sprintf("https://%s/dstsv2/%s/v2.0/.well-known/openid-configuration", authorityInfo.Host, authority.DSTSTenant), nil
} else if authorityInfo.ValidateAuthority && !authority.TrustedHost(authorityInfo.Host) {
resp, err := m.rest.Authority().AADInstanceDiscovery(ctx, authorityInfo)
if err != nil {
@ -134,7 +134,6 @@ func (m *authorityEndpoint) openIDConfigurationEndpoint(ctx context.Context, aut
return "", err
}
return resp.TenantDiscoveryEndpoint, nil
}
return authorityInfo.CanonicalAuthorityURI + "v2.0/.well-known/openid-configuration", nil

View file

@ -29,7 +29,7 @@ const (
)
func (es EndpointState) String() string {
return [...]string{"Uninitialized", "Attached", "AttachedSharing", "Detached", "Degraded", "Destroyed"}[es]
return [...]string{"Uninitialized", "Created", "Attached", "AttachedSharing", "Detached", "Degraded", "Destroyed"}[es]
}
// HNSEndpoint represents a network endpoint in HNS

View file

@ -188,7 +188,7 @@ func Open(ctx context.Context, options *Options) (_ *JobObject, err error) {
return nil, winapi.RtlNtStatusToDosError(status)
}
} else {
jobHandle, err = winapi.OpenJobObject(winapi.JOB_OBJECT_ALL_ACCESS, 0, unicodeJobName.Buffer)
jobHandle, err = winapi.OpenJobObject(winapi.JOB_OBJECT_ALL_ACCESS, false, unicodeJobName.Buffer)
if err != nil {
return nil, err
}
@ -523,12 +523,9 @@ func (job *JobObject) ApplyFileBinding(root, target string, readOnly bool) error
func isJobSilo(h windows.Handle) bool {
// None of the information from the structure that this info class expects will be used, this is just used as
// the call will fail if the job hasn't been upgraded to a silo so we can use this to tell when we open a job
// if it's a silo or not. Because none of the info matters simply define a dummy struct with the size that the call
// expects which is 16 bytes.
type isSiloObj struct {
_ [16]byte
}
var siloInfo isSiloObj
// if it's a silo or not. We still need to define the struct layout as expected by Win32, else the struct
// alignment might be different and the call will fail.
var siloInfo winapi.SILOOBJECT_BASIC_INFORMATION
err := winapi.QueryInformationJobObject(
h,
winapi.JobObjectSiloBasicInformation,

View file

@ -6,7 +6,7 @@ import (
"net"
"os"
"github.com/containerd/errdefs"
errdefs "github.com/containerd/errdefs/pkg/errgrpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

View file

@ -28,7 +28,7 @@ const (
// https://docs.microsoft.com/en-us/windows/win32/procthread/job-object-security-and-access-rights
const (
JOB_OBJECT_QUERY = 0x0004
JOB_OBJECT_ALL_ACCESS = 0x1F001F
JOB_OBJECT_ALL_ACCESS = 0x1F003F
)
// IO limit flags
@ -160,6 +160,21 @@ type JOBOBJECT_ASSOCIATE_COMPLETION_PORT struct {
CompletionPort windows.Handle
}
// typedef struct _SILOOBJECT_BASIC_INFORMATION {
// DWORD SiloId;
// DWORD SiloParentId;
// DWORD NumberOfProcesses;
// BOOLEAN IsInServerSilo;
// BYTE Reserved[3];
// } SILOOBJECT_BASIC_INFORMATION, *PSILOOBJECT_BASIC_INFORMATION;
type SILOOBJECT_BASIC_INFORMATION struct {
SiloID uint32
SiloParentID uint32
NumberOfProcesses uint32
IsInServerSilo bool
Reserved [3]uint8
}
// BOOL IsProcessInJob(
// HANDLE ProcessHandle,
// HANDLE JobHandle,
@ -184,7 +199,7 @@ type JOBOBJECT_ASSOCIATE_COMPLETION_PORT struct {
// LPCWSTR lpName
// );
//
//sys OpenJobObject(desiredAccess uint32, inheritHandle int32, lpName *uint16) (handle windows.Handle, err error) = kernel32.OpenJobObjectW
//sys OpenJobObject(desiredAccess uint32, inheritHandle bool, lpName *uint16) (handle windows.Handle, err error) = kernel32.OpenJobObjectW
// DWORD SetIoRateControlInformationJobObject(
// HANDLE hJob,

View file

@ -470,8 +470,12 @@ func LocalFree(ptr uintptr) {
return
}
func OpenJobObject(desiredAccess uint32, inheritHandle int32, lpName *uint16) (handle windows.Handle, err error) {
r0, _, e1 := syscall.SyscallN(procOpenJobObjectW.Addr(), uintptr(desiredAccess), uintptr(inheritHandle), uintptr(unsafe.Pointer(lpName)))
func OpenJobObject(desiredAccess uint32, inheritHandle bool, lpName *uint16) (handle windows.Handle, err error) {
var _p0 uint32
if inheritHandle {
_p0 = 1
}
r0, _, e1 := syscall.SyscallN(procOpenJobObjectW.Addr(), uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(lpName)))
handle = windows.Handle(r0)
if handle == 0 {
err = errnoErr(e1)

View file

@ -1,4 +1,4 @@
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Coverage Status][coveralls image]][coveralls]
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
@ -21,8 +21,6 @@ Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
[travis]: https://travis-ci.org/cenkalti/backoff
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master

View file

@ -71,6 +71,9 @@ type Clock interface {
Now() time.Time
}
// ExponentialBackOffOpts is a function type used to configure ExponentialBackOff options.
type ExponentialBackOffOpts func(*ExponentialBackOff)
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
@ -81,7 +84,7 @@ const (
)
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
func NewExponentialBackOff(opts ...ExponentialBackOffOpts) *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
@ -91,10 +94,62 @@ func NewExponentialBackOff() *ExponentialBackOff {
Stop: Stop,
Clock: SystemClock,
}
for _, fn := range opts {
fn(b)
}
b.Reset()
return b
}
// WithInitialInterval sets the initial interval between retries.
func WithInitialInterval(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.InitialInterval = duration
}
}
// WithRandomizationFactor sets the randomization factor to add jitter to intervals.
func WithRandomizationFactor(randomizationFactor float64) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.RandomizationFactor = randomizationFactor
}
}
// WithMultiplier sets the multiplier for increasing the interval after each retry.
func WithMultiplier(multiplier float64) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Multiplier = multiplier
}
}
// WithMaxInterval sets the maximum interval between retries.
func WithMaxInterval(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.MaxInterval = duration
}
}
// WithMaxElapsedTime sets the maximum total time for retries.
func WithMaxElapsedTime(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.MaxElapsedTime = duration
}
}
// WithRetryStopDuration sets the duration after which retries should stop.
func WithRetryStopDuration(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Stop = duration
}
}
// WithClockProvider sets the clock used to measure time.
func WithClockProvider(clock Clock) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Clock = clock
}
}
type systemClock struct{}
func (t systemClock) Now() time.Time {

View file

@ -21,9 +21,6 @@
//
// To detect an error class, use the IsXXX functions to tell whether an error
// is of a certain type.
//
// The functions ToGRPC and FromGRPC can be used to map server-side and
// client-side errors to the correct types.
package errdefs
import (
@ -36,57 +33,411 @@ import (
// Packages should return errors of these types when they want to instruct a
// client to take a particular action.
//
// For the most part, we just try to provide local grpc errors. Most conditions
// map very well to those defined by grpc.
// These errors map closely to grpc errors.
var (
ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
ErrInvalidArgument = errors.New("invalid argument")
ErrNotFound = errors.New("not found")
ErrAlreadyExists = errors.New("already exists")
ErrFailedPrecondition = errors.New("failed precondition")
ErrUnavailable = errors.New("unavailable")
ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
ErrUnknown = errUnknown{}
ErrInvalidArgument = errInvalidArgument{}
ErrNotFound = errNotFound{}
ErrAlreadyExists = errAlreadyExists{}
ErrPermissionDenied = errPermissionDenied{}
ErrResourceExhausted = errResourceExhausted{}
ErrFailedPrecondition = errFailedPrecondition{}
ErrConflict = errConflict{}
ErrNotModified = errNotModified{}
ErrAborted = errAborted{}
ErrOutOfRange = errOutOfRange{}
ErrNotImplemented = errNotImplemented{}
ErrInternal = errInternal{}
ErrUnavailable = errUnavailable{}
ErrDataLoss = errDataLoss{}
ErrUnauthenticated = errUnauthorized{}
)
// IsInvalidArgument returns true if the error is due to an invalid argument
func IsInvalidArgument(err error) bool {
return errors.Is(err, ErrInvalidArgument)
}
// IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool {
return errors.Is(err, ErrNotFound)
}
// IsAlreadyExists returns true if the error is due to an already existing
// metadata item
func IsAlreadyExists(err error) bool {
return errors.Is(err, ErrAlreadyExists)
}
// IsFailedPrecondition returns true if an operation could not proceed to the
// lack of a particular condition
func IsFailedPrecondition(err error) bool {
return errors.Is(err, ErrFailedPrecondition)
}
// IsUnavailable returns true if the error is due to a resource being unavailable
func IsUnavailable(err error) bool {
return errors.Is(err, ErrUnavailable)
}
// IsNotImplemented returns true if the error is due to not being implemented
func IsNotImplemented(err error) bool {
return errors.Is(err, ErrNotImplemented)
// cancelled maps to Moby's "ErrCancelled"
type cancelled interface {
Cancelled()
}
// IsCanceled returns true if the error is due to `context.Canceled`.
func IsCanceled(err error) bool {
return errors.Is(err, context.Canceled)
return errors.Is(err, context.Canceled) || isInterface[cancelled](err)
}
type errUnknown struct{}
func (errUnknown) Error() string { return "unknown" }
func (errUnknown) Unknown() {}
func (e errUnknown) WithMessage(msg string) error {
return customMessage{e, msg}
}
// unknown maps to Moby's "ErrUnknown"
type unknown interface {
Unknown()
}
// IsUnknown returns true if the error is due to an unknown error,
// unhandled condition or unexpected response.
func IsUnknown(err error) bool {
return errors.Is(err, errUnknown{}) || isInterface[unknown](err)
}
type errInvalidArgument struct{}
func (errInvalidArgument) Error() string { return "invalid argument" }
func (errInvalidArgument) InvalidParameter() {}
func (e errInvalidArgument) WithMessage(msg string) error {
return customMessage{e, msg}
}
// invalidParameter maps to Moby's "ErrInvalidParameter"
type invalidParameter interface {
InvalidParameter()
}
// IsInvalidArgument returns true if the error is due to an invalid argument
func IsInvalidArgument(err error) bool {
return errors.Is(err, ErrInvalidArgument) || isInterface[invalidParameter](err)
}
// deadlineExceed maps to Moby's "ErrDeadline"
type deadlineExceeded interface {
DeadlineExceeded()
}
// IsDeadlineExceeded returns true if the error is due to
// `context.DeadlineExceeded`.
func IsDeadlineExceeded(err error) bool {
return errors.Is(err, context.DeadlineExceeded)
return errors.Is(err, context.DeadlineExceeded) || isInterface[deadlineExceeded](err)
}
type errNotFound struct{}
func (errNotFound) Error() string { return "not found" }
func (errNotFound) NotFound() {}
func (e errNotFound) WithMessage(msg string) error {
return customMessage{e, msg}
}
// notFound maps to Moby's "ErrNotFound"
type notFound interface {
NotFound()
}
// IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool {
return errors.Is(err, ErrNotFound) || isInterface[notFound](err)
}
type errAlreadyExists struct{}
func (errAlreadyExists) Error() string { return "already exists" }
func (errAlreadyExists) AlreadyExists() {}
func (e errAlreadyExists) WithMessage(msg string) error {
return customMessage{e, msg}
}
type alreadyExists interface {
AlreadyExists()
}
// IsAlreadyExists returns true if the error is due to an already existing
// metadata item
func IsAlreadyExists(err error) bool {
return errors.Is(err, ErrAlreadyExists) || isInterface[alreadyExists](err)
}
type errPermissionDenied struct{}
func (errPermissionDenied) Error() string { return "permission denied" }
func (errPermissionDenied) Forbidden() {}
func (e errPermissionDenied) WithMessage(msg string) error {
return customMessage{e, msg}
}
// forbidden maps to Moby's "ErrForbidden"
type forbidden interface {
Forbidden()
}
// IsPermissionDenied returns true if the error is due to permission denied
// or forbidden (403) response
func IsPermissionDenied(err error) bool {
return errors.Is(err, ErrPermissionDenied) || isInterface[forbidden](err)
}
type errResourceExhausted struct{}
func (errResourceExhausted) Error() string { return "resource exhausted" }
func (errResourceExhausted) ResourceExhausted() {}
func (e errResourceExhausted) WithMessage(msg string) error {
return customMessage{e, msg}
}
type resourceExhausted interface {
ResourceExhausted()
}
// IsResourceExhausted returns true if the error is due to
// a lack of resources or too many attempts.
func IsResourceExhausted(err error) bool {
return errors.Is(err, errResourceExhausted{}) || isInterface[resourceExhausted](err)
}
type errFailedPrecondition struct{}
func (e errFailedPrecondition) Error() string { return "failed precondition" }
func (errFailedPrecondition) FailedPrecondition() {}
func (e errFailedPrecondition) WithMessage(msg string) error {
return customMessage{e, msg}
}
type failedPrecondition interface {
FailedPrecondition()
}
// IsFailedPrecondition returns true if an operation could not proceed due to
// the lack of a particular condition
func IsFailedPrecondition(err error) bool {
return errors.Is(err, errFailedPrecondition{}) || isInterface[failedPrecondition](err)
}
type errConflict struct{}
func (errConflict) Error() string { return "conflict" }
func (errConflict) Conflict() {}
func (e errConflict) WithMessage(msg string) error {
return customMessage{e, msg}
}
// conflict maps to Moby's "ErrConflict"
type conflict interface {
Conflict()
}
// IsConflict returns true if an operation could not proceed due to
// a conflict.
func IsConflict(err error) bool {
return errors.Is(err, errConflict{}) || isInterface[conflict](err)
}
type errNotModified struct{}
func (errNotModified) Error() string { return "not modified" }
func (errNotModified) NotModified() {}
func (e errNotModified) WithMessage(msg string) error {
return customMessage{e, msg}
}
// notModified maps to Moby's "ErrNotModified"
type notModified interface {
NotModified()
}
// IsNotModified returns true if an operation could not proceed due
// to an object not modified from a previous state.
func IsNotModified(err error) bool {
return errors.Is(err, errNotModified{}) || isInterface[notModified](err)
}
type errAborted struct{}
func (errAborted) Error() string { return "aborted" }
func (errAborted) Aborted() {}
func (e errAborted) WithMessage(msg string) error {
return customMessage{e, msg}
}
type aborted interface {
Aborted()
}
// IsAborted returns true if an operation was aborted.
func IsAborted(err error) bool {
return errors.Is(err, errAborted{}) || isInterface[aborted](err)
}
type errOutOfRange struct{}
func (errOutOfRange) Error() string { return "out of range" }
func (errOutOfRange) OutOfRange() {}
func (e errOutOfRange) WithMessage(msg string) error {
return customMessage{e, msg}
}
type outOfRange interface {
OutOfRange()
}
// IsOutOfRange returns true if an operation could not proceed due
// to data being out of the expected range.
func IsOutOfRange(err error) bool {
return errors.Is(err, errOutOfRange{}) || isInterface[outOfRange](err)
}
type errNotImplemented struct{}
func (errNotImplemented) Error() string { return "not implemented" }
func (errNotImplemented) NotImplemented() {}
func (e errNotImplemented) WithMessage(msg string) error {
return customMessage{e, msg}
}
// notImplemented maps to Moby's "ErrNotImplemented"
type notImplemented interface {
NotImplemented()
}
// IsNotImplemented returns true if the error is due to not being implemented
func IsNotImplemented(err error) bool {
return errors.Is(err, errNotImplemented{}) || isInterface[notImplemented](err)
}
type errInternal struct{}
func (errInternal) Error() string { return "internal" }
func (errInternal) System() {}
func (e errInternal) WithMessage(msg string) error {
return customMessage{e, msg}
}
// system maps to Moby's "ErrSystem"
type system interface {
System()
}
// IsInternal returns true if the error returns to an internal or system error
func IsInternal(err error) bool {
return errors.Is(err, errInternal{}) || isInterface[system](err)
}
type errUnavailable struct{}
func (errUnavailable) Error() string { return "unavailable" }
func (errUnavailable) Unavailable() {}
func (e errUnavailable) WithMessage(msg string) error {
return customMessage{e, msg}
}
// unavailable maps to Moby's "ErrUnavailable"
type unavailable interface {
Unavailable()
}
// IsUnavailable returns true if the error is due to a resource being unavailable
func IsUnavailable(err error) bool {
return errors.Is(err, errUnavailable{}) || isInterface[unavailable](err)
}
type errDataLoss struct{}
func (errDataLoss) Error() string { return "data loss" }
func (errDataLoss) DataLoss() {}
func (e errDataLoss) WithMessage(msg string) error {
return customMessage{e, msg}
}
// dataLoss maps to Moby's "ErrDataLoss"
type dataLoss interface {
DataLoss()
}
// IsDataLoss returns true if data during an operation was lost or corrupted
func IsDataLoss(err error) bool {
return errors.Is(err, errDataLoss{}) || isInterface[dataLoss](err)
}
type errUnauthorized struct{}
func (errUnauthorized) Error() string { return "unauthorized" }
func (errUnauthorized) Unauthorized() {}
func (e errUnauthorized) WithMessage(msg string) error {
return customMessage{e, msg}
}
// unauthorized maps to Moby's "ErrUnauthorized"
type unauthorized interface {
Unauthorized()
}
// IsUnauthorized returns true if the error indicates that the user was
// unauthenticated or unauthorized.
func IsUnauthorized(err error) bool {
return errors.Is(err, errUnauthorized{}) || isInterface[unauthorized](err)
}
func isInterface[T any](err error) bool {
for {
switch x := err.(type) {
case T:
return true
case customMessage:
err = x.err
case interface{ Unwrap() error }:
err = x.Unwrap()
if err == nil {
return false
}
case interface{ Unwrap() []error }:
for _, err := range x.Unwrap() {
if isInterface[T](err) {
return true
}
}
return false
default:
return false
}
}
}
// customMessage is used to provide a defined error with a custom message.
// The message is not wrapped but can be compared by the `Is(error) bool` interface.
type customMessage struct {
err error
msg string
}
func (c customMessage) Is(err error) bool {
return c.err == err
}
func (c customMessage) As(target any) bool {
return errors.As(c.err, target)
}
func (c customMessage) Error() string {
return c.msg
}

View file

@ -1,147 +0,0 @@
/*
Copyright The containerd Authors.
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 errdefs
import (
"context"
"fmt"
"strings"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ToGRPC will attempt to map the backend containerd error into a grpc error,
// using the original error message as a description.
//
// Further information may be extracted from certain errors depending on their
// type.
//
// If the error is unmapped, the original error will be returned to be handled
// by the regular grpc error handling stack.
func ToGRPC(err error) error {
if err == nil {
return nil
}
if isGRPCError(err) {
// error has already been mapped to grpc
return err
}
switch {
case IsInvalidArgument(err):
return status.Errorf(codes.InvalidArgument, err.Error())
case IsNotFound(err):
return status.Errorf(codes.NotFound, err.Error())
case IsAlreadyExists(err):
return status.Errorf(codes.AlreadyExists, err.Error())
case IsFailedPrecondition(err):
return status.Errorf(codes.FailedPrecondition, err.Error())
case IsUnavailable(err):
return status.Errorf(codes.Unavailable, err.Error())
case IsNotImplemented(err):
return status.Errorf(codes.Unimplemented, err.Error())
case IsCanceled(err):
return status.Errorf(codes.Canceled, err.Error())
case IsDeadlineExceeded(err):
return status.Errorf(codes.DeadlineExceeded, err.Error())
}
return err
}
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
// and combining it with the target error string.
//
// This is equivalent to errdefs.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
func ToGRPCf(err error, format string, args ...interface{}) error {
return ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
}
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
func FromGRPC(err error) error {
if err == nil {
return nil
}
var cls error // divide these into error classes, becomes the cause
switch code(err) {
case codes.InvalidArgument:
cls = ErrInvalidArgument
case codes.AlreadyExists:
cls = ErrAlreadyExists
case codes.NotFound:
cls = ErrNotFound
case codes.Unavailable:
cls = ErrUnavailable
case codes.FailedPrecondition:
cls = ErrFailedPrecondition
case codes.Unimplemented:
cls = ErrNotImplemented
case codes.Canceled:
cls = context.Canceled
case codes.DeadlineExceeded:
cls = context.DeadlineExceeded
default:
cls = ErrUnknown
}
msg := rebaseMessage(cls, err)
if msg != "" {
err = fmt.Errorf("%s: %w", msg, cls)
} else {
err = cls
}
return err
}
// rebaseMessage removes the repeats for an error at the end of an error
// string. This will happen when taking an error over grpc then remapping it.
//
// Effectively, we just remove the string of cls from the end of err if it
// appears there.
func rebaseMessage(cls error, err error) string {
desc := errDesc(err)
clss := cls.Error()
if desc == clss {
return ""
}
return strings.TrimSuffix(desc, ": "+clss)
}
func isGRPCError(err error) bool {
_, ok := status.FromError(err)
return ok
}
func code(err error) codes.Code {
if s, ok := status.FromError(err); ok {
return s.Code()
}
return codes.Unknown
}
func errDesc(err error) string {
if s, ok := status.FromError(err); ok {
return s.Message()
}
return err.Error()
}

191
vendor/github.com/containerd/errdefs/pkg/LICENSE generated vendored Normal file
View file

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
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
https://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.

View file

@ -0,0 +1,353 @@
/*
Copyright The containerd Authors.
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 errgrpc provides utility functions for translating errors to
// and from a gRPC context.
//
// The functions ToGRPC and ToNative can be used to map server-side and
// client-side errors to the correct types.
package errgrpc
import (
"context"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/protoadapt"
"google.golang.org/protobuf/types/known/anypb"
"github.com/containerd/typeurl/v2"
"github.com/containerd/errdefs"
"github.com/containerd/errdefs/pkg/internal/cause"
"github.com/containerd/errdefs/pkg/internal/types"
)
// ToGRPC will attempt to map the error into a grpc error, from the error types
// defined in the the errdefs package and attempign to preserve the original
// description. Any type which does not resolve to a defined error type will
// be assigned the unknown error code.
//
// Further information may be extracted from certain errors depending on their
// type. The grpc error details will be used to attempt to preserve as much of
// the error structures and types as possible.
//
// Errors which can be marshaled using protobuf or typeurl will be considered
// for including as GRPC error details.
// Additionally, use the following interfaces in errors to preserve custom types:
//
// WrapError(error) error - Used to wrap the previous error
// JoinErrors(...error) error - Used to join all previous errors
// CollapseError() - Used for errors which carry information but
// should not have their error message shown.
func ToGRPC(err error) error {
if err == nil {
return nil
}
if _, ok := status.FromError(err); ok {
// error has already been mapped to grpc
return err
}
st := statusFromError(err)
if st != nil {
if details := errorDetails(err, false); len(details) > 0 {
if ds, _ := st.WithDetails(details...); ds != nil {
st = ds
}
}
err = st.Err()
}
return err
}
func statusFromError(err error) *status.Status {
switch errdefs.Resolve(err) {
case errdefs.ErrInvalidArgument:
return status.New(codes.InvalidArgument, err.Error())
case errdefs.ErrNotFound:
return status.New(codes.NotFound, err.Error())
case errdefs.ErrAlreadyExists:
return status.New(codes.AlreadyExists, err.Error())
case errdefs.ErrPermissionDenied:
return status.New(codes.PermissionDenied, err.Error())
case errdefs.ErrResourceExhausted:
return status.New(codes.ResourceExhausted, err.Error())
case errdefs.ErrFailedPrecondition, errdefs.ErrConflict, errdefs.ErrNotModified:
return status.New(codes.FailedPrecondition, err.Error())
case errdefs.ErrAborted:
return status.New(codes.Aborted, err.Error())
case errdefs.ErrOutOfRange:
return status.New(codes.OutOfRange, err.Error())
case errdefs.ErrNotImplemented:
return status.New(codes.Unimplemented, err.Error())
case errdefs.ErrInternal:
return status.New(codes.Internal, err.Error())
case errdefs.ErrUnavailable:
return status.New(codes.Unavailable, err.Error())
case errdefs.ErrDataLoss:
return status.New(codes.DataLoss, err.Error())
case errdefs.ErrUnauthenticated:
return status.New(codes.Unauthenticated, err.Error())
case context.DeadlineExceeded:
return status.New(codes.DeadlineExceeded, err.Error())
case context.Canceled:
return status.New(codes.Canceled, err.Error())
case errdefs.ErrUnknown:
return status.New(codes.Unknown, err.Error())
}
return nil
}
// errorDetails returns an array of errors which make up the provided error.
// If firstIncluded is true, then all encodable errors will be used, otherwise
// the first error in an error list will be not be used, to account for the
// the base status error which details are added to via wrap or join.
//
// The errors are ordered in way that they can be applied in order by either
// wrapping or joining the errors to recreate an error with the same structure
// when `WrapError` and `JoinErrors` interfaces are used.
//
// The intent is that when re-applying the errors to create a single error, the
// results of calls to `Error()`, `errors.Is`, `errors.As`, and "%+v" formatting
// is the same as the original error.
func errorDetails(err error, firstIncluded bool) []protoadapt.MessageV1 {
switch uerr := err.(type) {
case interface{ Unwrap() error }:
details := errorDetails(uerr.Unwrap(), firstIncluded)
// If the type is able to wrap, then include if proto
if _, ok := err.(interface{ WrapError(error) error }); ok {
// Get proto message
if protoErr := toProtoMessage(err); protoErr != nil {
details = append(details, protoErr)
}
}
return details
case interface{ Unwrap() []error }:
var details []protoadapt.MessageV1
for i, e := range uerr.Unwrap() {
details = append(details, errorDetails(e, firstIncluded || i > 0)...)
}
if _, ok := err.(interface{ JoinErrors(...error) error }); ok {
// Get proto message
if protoErr := toProtoMessage(err); protoErr != nil {
details = append(details, protoErr)
}
}
return details
}
if firstIncluded {
if protoErr := toProtoMessage(err); protoErr != nil {
return []protoadapt.MessageV1{protoErr}
}
if gs, ok := status.FromError(ToGRPC(err)); ok {
return []protoadapt.MessageV1{gs.Proto()}
}
// TODO: Else include unknown extra error type?
}
return nil
}
func toProtoMessage(err error) protoadapt.MessageV1 {
// Do not double encode proto messages, otherwise use Any
if pm, ok := err.(protoadapt.MessageV1); ok {
return pm
}
if pm, ok := err.(proto.Message); ok {
return protoadapt.MessageV1Of(pm)
}
if reflect.TypeOf(err).Kind() == reflect.Ptr {
a, aerr := typeurl.MarshalAny(err)
if aerr == nil {
return &anypb.Any{
TypeUrl: a.GetTypeUrl(),
Value: a.GetValue(),
}
}
}
return nil
}
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
// and combining it with the target error string.
//
// This is equivalent to grpc.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
func ToGRPCf(err error, format string, args ...interface{}) error {
return ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
}
// ToNative returns the underlying error from a grpc service based on the grpc
// error code. The grpc details are used to add wrap the error in more context
// or support multiple errors.
func ToNative(err error) error {
if err == nil {
return nil
}
s, isGRPC := status.FromError(err)
var (
desc string
code codes.Code
)
if isGRPC {
desc = s.Message()
code = s.Code()
} else {
desc = err.Error()
code = codes.Unknown
}
var cls error // divide these into error classes, becomes the cause
switch code {
case codes.InvalidArgument:
cls = errdefs.ErrInvalidArgument
case codes.AlreadyExists:
cls = errdefs.ErrAlreadyExists
case codes.NotFound:
cls = errdefs.ErrNotFound
case codes.Unavailable:
cls = errdefs.ErrUnavailable
case codes.FailedPrecondition:
// TODO: Has suffix is not sufficient for conflict and not modified
// Message should start with ": " or be at beginning of a line
// Message should end with ": " or be at the end of a line
// Compile a regex
if desc == errdefs.ErrConflict.Error() || strings.HasSuffix(desc, ": "+errdefs.ErrConflict.Error()) {
cls = errdefs.ErrConflict
} else if desc == errdefs.ErrNotModified.Error() || strings.HasSuffix(desc, ": "+errdefs.ErrNotModified.Error()) {
cls = errdefs.ErrNotModified
} else {
cls = errdefs.ErrFailedPrecondition
}
case codes.Unimplemented:
cls = errdefs.ErrNotImplemented
case codes.Canceled:
cls = context.Canceled
case codes.DeadlineExceeded:
cls = context.DeadlineExceeded
case codes.Aborted:
cls = errdefs.ErrAborted
case codes.Unauthenticated:
cls = errdefs.ErrUnauthenticated
case codes.PermissionDenied:
cls = errdefs.ErrPermissionDenied
case codes.Internal:
cls = errdefs.ErrInternal
case codes.DataLoss:
cls = errdefs.ErrDataLoss
case codes.OutOfRange:
cls = errdefs.ErrOutOfRange
case codes.ResourceExhausted:
cls = errdefs.ErrResourceExhausted
default:
if idx := strings.LastIndex(desc, cause.UnexpectedStatusPrefix); idx > 0 {
if status, uerr := strconv.Atoi(desc[idx+len(cause.UnexpectedStatusPrefix):]); uerr == nil && status >= 200 && status < 600 {
cls = cause.ErrUnexpectedStatus{Status: status}
}
}
if cls == nil {
cls = errdefs.ErrUnknown
}
}
msg := rebaseMessage(cls, desc)
if msg == "" {
err = cls
} else if msg != desc {
err = fmt.Errorf("%s: %w", msg, cls)
} else if wm, ok := cls.(interface{ WithMessage(string) error }); ok {
err = wm.WithMessage(msg)
} else {
err = fmt.Errorf("%s: %w", msg, cls)
}
if isGRPC {
errs := []error{err}
for _, a := range s.Details() {
var derr error
// First decode error if needed
if s, ok := a.(*spb.Status); ok {
derr = ToNative(status.ErrorProto(s))
} else if e, ok := a.(error); ok {
derr = e
} else if dany, ok := a.(typeurl.Any); ok {
i, uerr := typeurl.UnmarshalAny(dany)
if uerr == nil {
if e, ok = i.(error); ok {
derr = e
} else {
derr = fmt.Errorf("non-error unmarshalled detail: %v", i)
}
} else {
derr = fmt.Errorf("error of type %q with failure to unmarshal: %v", dany.GetTypeUrl(), uerr)
}
} else {
derr = fmt.Errorf("non-error detail: %v", a)
}
switch werr := derr.(type) {
case interface{ WrapError(error) error }:
errs[len(errs)-1] = werr.WrapError(errs[len(errs)-1])
case interface{ JoinErrors(...error) error }:
// TODO: Consider whether this should support joining a subset
errs[0] = werr.JoinErrors(errs...)
case interface{ CollapseError() }:
errs[len(errs)-1] = types.CollapsedError(errs[len(errs)-1], derr)
default:
errs = append(errs, derr)
}
}
if len(errs) > 1 {
err = errors.Join(errs...)
} else {
err = errs[0]
}
}
return err
}
// rebaseMessage removes the repeats for an error at the end of an error
// string. This will happen when taking an error over grpc then remapping it.
//
// Effectively, we just remove the string of cls from the end of err if it
// appears there.
func rebaseMessage(cls error, desc string) string {
clss := cls.Error()
if desc == clss {
return ""
}
return strings.TrimSuffix(desc, ": "+clss)
}

View file

@ -0,0 +1,33 @@
/*
Copyright The containerd Authors.
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 cause is used to define root causes for errors
// common to errors packages like grpc and http.
package cause
import "fmt"
type ErrUnexpectedStatus struct {
Status int
}
const UnexpectedStatusPrefix = "unexpected status "
func (e ErrUnexpectedStatus) Error() string {
return fmt.Sprintf("%s%d", UnexpectedStatusPrefix, e.Status)
}
func (ErrUnexpectedStatus) Unknown() {}

View file

@ -0,0 +1,57 @@
/*
Copyright The containerd Authors.
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 types
import "fmt"
// CollapsibleError indicates the error should be collapsed
type CollapsibleError interface {
CollapseError()
}
// CollapsedError returns a new error with the collapsed
// error returned on unwrapped or when formatted with "%+v"
func CollapsedError(err error, collapsed ...error) error {
return collapsedError{err, collapsed}
}
type collapsedError struct {
error
collapsed []error
}
func (c collapsedError) Unwrap() []error {
return append([]error{c.error}, c.collapsed...)
}
func (c collapsedError) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v", c.error)
for _, err := range c.collapsed {
fmt.Fprintf(s, "\n%+v", err)
}
return
}
fallthrough
case 's':
fmt.Fprint(s, c.Error())
case 'q':
fmt.Fprintf(s, "%q", c.Error())
}
}

147
vendor/github.com/containerd/errdefs/resolve.go generated vendored Normal file
View file

@ -0,0 +1,147 @@
/*
Copyright The containerd Authors.
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 errdefs
import "context"
// Resolve returns the first error found in the error chain which matches an
// error defined in this package or context error. A raw, unwrapped error is
// returned or ErrUnknown if no matching error is found.
//
// This is useful for determining a response code based on the outermost wrapped
// error rather than the original cause. For example, a not found error deep
// in the code may be wrapped as an invalid argument. When determining status
// code from Is* functions, the depth or ordering of the error is not
// considered.
//
// The search order is depth first, a wrapped error returned from any part of
// the chain from `Unwrap() error` will be returned before any joined errors
// as returned by `Unwrap() []error`.
func Resolve(err error) error {
if err == nil {
return nil
}
err = firstError(err)
if err == nil {
err = ErrUnknown
}
return err
}
func firstError(err error) error {
for {
switch err {
case ErrUnknown,
ErrInvalidArgument,
ErrNotFound,
ErrAlreadyExists,
ErrPermissionDenied,
ErrResourceExhausted,
ErrFailedPrecondition,
ErrConflict,
ErrNotModified,
ErrAborted,
ErrOutOfRange,
ErrNotImplemented,
ErrInternal,
ErrUnavailable,
ErrDataLoss,
ErrUnauthenticated,
context.DeadlineExceeded,
context.Canceled:
return err
}
switch e := err.(type) {
case customMessage:
err = e.err
case unknown:
return ErrUnknown
case invalidParameter:
return ErrInvalidArgument
case notFound:
return ErrNotFound
case alreadyExists:
return ErrAlreadyExists
case forbidden:
return ErrPermissionDenied
case resourceExhausted:
return ErrResourceExhausted
case failedPrecondition:
return ErrFailedPrecondition
case conflict:
return ErrConflict
case notModified:
return ErrNotModified
case aborted:
return ErrAborted
case errOutOfRange:
return ErrOutOfRange
case notImplemented:
return ErrNotImplemented
case system:
return ErrInternal
case unavailable:
return ErrUnavailable
case dataLoss:
return ErrDataLoss
case unauthorized:
return ErrUnauthenticated
case deadlineExceeded:
return context.DeadlineExceeded
case cancelled:
return context.Canceled
case interface{ Unwrap() error }:
err = e.Unwrap()
if err == nil {
return nil
}
case interface{ Unwrap() []error }:
for _, ue := range e.Unwrap() {
if fe := firstError(ue); fe != nil {
return fe
}
}
return nil
case interface{ Is(error) bool }:
for _, target := range []error{ErrUnknown,
ErrInvalidArgument,
ErrNotFound,
ErrAlreadyExists,
ErrPermissionDenied,
ErrResourceExhausted,
ErrFailedPrecondition,
ErrConflict,
ErrNotModified,
ErrAborted,
ErrOutOfRange,
ErrNotImplemented,
ErrInternal,
ErrUnavailable,
ErrDataLoss,
ErrUnauthenticated,
context.DeadlineExceeded,
context.Canceled} {
if e.Is(target) {
return target
}
}
return nil
default:
return nil
}
}
}

2
vendor/github.com/containerd/typeurl/v2/.gitignore generated vendored Normal file
View file

@ -0,0 +1,2 @@
*.test
coverage.txt

191
vendor/github.com/containerd/typeurl/v2/LICENSE generated vendored Normal file
View file

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
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
https://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.

20
vendor/github.com/containerd/typeurl/v2/README.md generated vendored Normal file
View file

@ -0,0 +1,20 @@
# typeurl
[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/typeurl)](https://pkg.go.dev/github.com/containerd/typeurl)
[![Build Status](https://github.com/containerd/typeurl/workflows/CI/badge.svg)](https://github.com/containerd/typeurl/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/containerd/typeurl/branch/main/graph/badge.svg)](https://codecov.io/gh/containerd/typeurl)
[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/typeurl)](https://goreportcard.com/report/github.com/containerd/typeurl)
A Go package for managing the registration, marshaling, and unmarshaling of encoded types.
This package helps when types are sent over a ttrpc/GRPC API and marshaled as a protobuf [Any](https://pkg.go.dev/google.golang.org/protobuf@v1.27.1/types/known/anypb#Any)
## Project details
**typeurl** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

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