1// Copyright 2023 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15syntax = "proto3"; 16 17package google.cloud.gkehub.v1; 18 19import "google/api/annotations.proto"; 20import "google/api/client.proto"; 21import "google/api/field_behavior.proto"; 22import "google/api/resource.proto"; 23import "google/cloud/gkehub/v1/feature.proto"; 24import "google/cloud/gkehub/v1/membership.proto"; 25import "google/longrunning/operations.proto"; 26import "google/protobuf/empty.proto"; 27import "google/protobuf/field_mask.proto"; 28import "google/protobuf/timestamp.proto"; 29 30option csharp_namespace = "Google.Cloud.GkeHub.V1"; 31option go_package = "cloud.google.com/go/gkehub/apiv1/gkehubpb;gkehubpb"; 32option java_multiple_files = true; 33option java_outer_classname = "ServiceProto"; 34option java_package = "com.google.cloud.gkehub.v1"; 35option php_namespace = "Google\\Cloud\\GkeHub\\V1"; 36option ruby_package = "Google::Cloud::GkeHub::V1"; 37 38// The GKE Hub service handles the registration of many Kubernetes clusters to 39// Google Cloud, and the management of multi-cluster features over those 40// clusters. 41// 42// The GKE Hub service operates on the following resources: 43// 44// * [Membership][google.cloud.gkehub.v1.Membership] 45// * [Feature][google.cloud.gkehub.v1.Feature] 46// 47// GKE Hub is currently available in the global region and all regions in 48// https://cloud.google.com/compute/docs/regions-zones. Feature is only 49// available in global region while membership is global region and all the 50// regions. 51// 52// **Membership management may be non-trivial:** it is recommended to use one 53// of the Google-provided client libraries or tools where possible when working 54// with Membership resources. 55service GkeHub { 56 option (google.api.default_host) = "gkehub.googleapis.com"; 57 option (google.api.oauth_scopes) = 58 "https://www.googleapis.com/auth/cloud-platform"; 59 60 // Lists Memberships in a given project and location. 61 rpc ListMemberships(ListMembershipsRequest) 62 returns (ListMembershipsResponse) { 63 option (google.api.http) = { 64 get: "/v1/{parent=projects/*/locations/*}/memberships" 65 }; 66 option (google.api.method_signature) = "parent"; 67 } 68 69 // Lists Features in a given project and location. 70 rpc ListFeatures(ListFeaturesRequest) returns (ListFeaturesResponse) { 71 option (google.api.http) = { 72 get: "/v1/{parent=projects/*/locations/*}/features" 73 }; 74 option (google.api.method_signature) = "parent"; 75 } 76 77 // Gets the details of a Membership. 78 rpc GetMembership(GetMembershipRequest) returns (Membership) { 79 option (google.api.http) = { 80 get: "/v1/{name=projects/*/locations/*/memberships/*}" 81 }; 82 option (google.api.method_signature) = "name"; 83 } 84 85 // Gets details of a single Feature. 86 rpc GetFeature(GetFeatureRequest) returns (Feature) { 87 option (google.api.http) = { 88 get: "/v1/{name=projects/*/locations/*/features/*}" 89 }; 90 option (google.api.method_signature) = "name"; 91 } 92 93 // Creates a new Membership. 94 // 95 // **This is currently only supported for GKE clusters on Google Cloud**. 96 // To register other clusters, follow the instructions at 97 // https://cloud.google.com/anthos/multicluster-management/connect/registering-a-cluster. 98 rpc CreateMembership(CreateMembershipRequest) 99 returns (google.longrunning.Operation) { 100 option (google.api.http) = { 101 post: "/v1/{parent=projects/*/locations/*}/memberships" 102 body: "resource" 103 }; 104 option (google.api.method_signature) = "parent,resource,membership_id"; 105 option (google.longrunning.operation_info) = { 106 response_type: "Membership" 107 metadata_type: "OperationMetadata" 108 }; 109 } 110 111 // Adds a new Feature. 112 rpc CreateFeature(CreateFeatureRequest) 113 returns (google.longrunning.Operation) { 114 option (google.api.http) = { 115 post: "/v1/{parent=projects/*/locations/*}/features" 116 body: "resource" 117 }; 118 option (google.api.method_signature) = "parent,resource,feature_id"; 119 option (google.longrunning.operation_info) = { 120 response_type: "Feature" 121 metadata_type: "OperationMetadata" 122 }; 123 } 124 125 // Removes a Membership. 126 // 127 // **This is currently only supported for GKE clusters on Google Cloud**. 128 // To unregister other clusters, follow the instructions at 129 // https://cloud.google.com/anthos/multicluster-management/connect/unregistering-a-cluster. 130 rpc DeleteMembership(DeleteMembershipRequest) 131 returns (google.longrunning.Operation) { 132 option (google.api.http) = { 133 delete: "/v1/{name=projects/*/locations/*/memberships/*}" 134 }; 135 option (google.api.method_signature) = "name"; 136 option (google.longrunning.operation_info) = { 137 response_type: "google.protobuf.Empty" 138 metadata_type: "OperationMetadata" 139 }; 140 } 141 142 // Removes a Feature. 143 rpc DeleteFeature(DeleteFeatureRequest) 144 returns (google.longrunning.Operation) { 145 option (google.api.http) = { 146 delete: "/v1/{name=projects/*/locations/*/features/*}" 147 }; 148 option (google.api.method_signature) = "name"; 149 option (google.longrunning.operation_info) = { 150 response_type: "google.protobuf.Empty" 151 metadata_type: "OperationMetadata" 152 }; 153 } 154 155 // Updates an existing Membership. 156 rpc UpdateMembership(UpdateMembershipRequest) 157 returns (google.longrunning.Operation) { 158 option (google.api.http) = { 159 patch: "/v1/{name=projects/*/locations/*/memberships/*}" 160 body: "resource" 161 }; 162 option (google.api.method_signature) = "name,resource,update_mask"; 163 option (google.longrunning.operation_info) = { 164 response_type: "Membership" 165 metadata_type: "OperationMetadata" 166 }; 167 } 168 169 // Updates an existing Feature. 170 rpc UpdateFeature(UpdateFeatureRequest) 171 returns (google.longrunning.Operation) { 172 option (google.api.http) = { 173 patch: "/v1/{name=projects/*/locations/*/features/*}" 174 body: "resource" 175 }; 176 option (google.api.method_signature) = "name,resource,update_mask"; 177 option (google.longrunning.operation_info) = { 178 response_type: "Feature" 179 metadata_type: "OperationMetadata" 180 }; 181 } 182 183 // Generates the manifest for deployment of the GKE connect agent. 184 // 185 // **This method is used internally by Google-provided libraries.** 186 // Most clients should not need to call this method directly. 187 rpc GenerateConnectManifest(GenerateConnectManifestRequest) 188 returns (GenerateConnectManifestResponse) { 189 option (google.api.http) = { 190 get: "/v1/{name=projects/*/locations/*/memberships/*}:generateConnectManifest" 191 }; 192 } 193} 194 195// Request message for `GkeHub.ListMemberships` method. 196message ListMembershipsRequest { 197 // Required. The parent (project and location) where the Memberships will be 198 // listed. Specified in the format `projects/*/locations/*`. 199 // `projects/*/locations/-` list memberships in all the regions. 200 string parent = 1 [ 201 (google.api.field_behavior) = REQUIRED, 202 (google.api.resource_reference) = { 203 child_type: "gkehub.googleapis.com/Membership" 204 } 205 ]; 206 207 // Optional. When requesting a 'page' of resources, `page_size` specifies 208 // number of resources to return. If unspecified or set to 0, all resources 209 // will be returned. 210 int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; 211 212 // Optional. Token returned by previous call to `ListMemberships` which 213 // specifies the position in the list from where to continue listing the 214 // resources. 215 string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; 216 217 // Optional. Lists Memberships that match the filter expression, following the 218 // syntax outlined in https://google.aip.dev/160. 219 // 220 // Examples: 221 // 222 // - Name is `bar` in project `foo-proj` and location `global`: 223 // 224 // name = "projects/foo-proj/locations/global/membership/bar" 225 // 226 // - Memberships that have a label called `foo`: 227 // 228 // labels.foo:* 229 // 230 // - Memberships that have a label called `foo` whose value is `bar`: 231 // 232 // labels.foo = bar 233 // 234 // - Memberships in the CREATING state: 235 // 236 // state = CREATING 237 string filter = 4 [(google.api.field_behavior) = OPTIONAL]; 238 239 // Optional. One or more fields to compare and use to sort the output. 240 // See https://google.aip.dev/132#ordering. 241 string order_by = 5 [(google.api.field_behavior) = OPTIONAL]; 242} 243 244// Response message for the `GkeHub.ListMemberships` method. 245message ListMembershipsResponse { 246 // The list of matching Memberships. 247 repeated Membership resources = 1; 248 249 // A token to request the next page of resources from the 250 // `ListMemberships` method. The value of an empty string means that 251 // there are no more resources to return. 252 string next_page_token = 2; 253 254 // List of locations that could not be reached while fetching this list. 255 repeated string unreachable = 3; 256} 257 258// Request message for `GkeHub.GetMembership` method. 259message GetMembershipRequest { 260 // Required. The Membership resource name in the format 261 // `projects/*/locations/*/memberships/*`. 262 string name = 1 [ 263 (google.api.field_behavior) = REQUIRED, 264 (google.api.resource_reference) = { 265 type: "gkehub.googleapis.com/Membership" 266 } 267 ]; 268} 269 270// Request message for the `GkeHub.CreateMembership` method. 271message CreateMembershipRequest { 272 // Required. The parent (project and location) where the Memberships will be 273 // created. Specified in the format `projects/*/locations/*`. 274 string parent = 1 [ 275 (google.api.field_behavior) = REQUIRED, 276 (google.api.resource_reference) = { 277 child_type: "gkehub.googleapis.com/Membership" 278 } 279 ]; 280 281 // Required. Client chosen ID for the membership. `membership_id` must be a 282 // valid RFC 1123 compliant DNS label: 283 // 284 // 1. At most 63 characters in length 285 // 2. It must consist of lower case alphanumeric characters or `-` 286 // 3. It must start and end with an alphanumeric character 287 // 288 // Which can be expressed as the regex: `[a-z0-9]([-a-z0-9]*[a-z0-9])?`, 289 // with a maximum length of 63 characters. 290 string membership_id = 2 [(google.api.field_behavior) = REQUIRED]; 291 292 // Required. The membership to create. 293 Membership resource = 3 [(google.api.field_behavior) = REQUIRED]; 294 295 // Optional. A request ID to identify requests. Specify a unique request ID 296 // so that if you must retry your request, the server will know to ignore 297 // the request if it has already been completed. The server will guarantee 298 // that for at least 60 minutes after the first request. 299 // 300 // For example, consider a situation where you make an initial request and 301 // the request times out. If you make the request again with the same request 302 // ID, the server can check if original operation with the same request ID 303 // was received, and if so, will ignore the second request. This prevents 304 // clients from accidentally creating duplicate commitments. 305 // 306 // The request ID must be a valid UUID with the exception that zero UUID is 307 // not supported (00000000-0000-0000-0000-000000000000). 308 string request_id = 4 [(google.api.field_behavior) = OPTIONAL]; 309} 310 311// Request message for `GkeHub.DeleteMembership` method. 312message DeleteMembershipRequest { 313 // Required. The Membership resource name in the format 314 // `projects/*/locations/*/memberships/*`. 315 string name = 1 [ 316 (google.api.field_behavior) = REQUIRED, 317 (google.api.resource_reference) = { 318 type: "gkehub.googleapis.com/Membership" 319 } 320 ]; 321 322 // Optional. A request ID to identify requests. Specify a unique request ID 323 // so that if you must retry your request, the server will know to ignore 324 // the request if it has already been completed. The server will guarantee 325 // that for at least 60 minutes after the first request. 326 // 327 // For example, consider a situation where you make an initial request and 328 // the request times out. If you make the request again with the same request 329 // ID, the server can check if original operation with the same request ID 330 // was received, and if so, will ignore the second request. This prevents 331 // clients from accidentally creating duplicate commitments. 332 // 333 // The request ID must be a valid UUID with the exception that zero UUID is 334 // not supported (00000000-0000-0000-0000-000000000000). 335 string request_id = 2 [(google.api.field_behavior) = OPTIONAL]; 336 337 // Optional. If set to true, any subresource from this Membership will also be 338 // deleted. Otherwise, the request will only work if the Membership has no 339 // subresource. 340 bool force = 3 [(google.api.field_behavior) = OPTIONAL]; 341} 342 343// Request message for `GkeHub.UpdateMembership` method. 344message UpdateMembershipRequest { 345 // Required. The Membership resource name in the format 346 // `projects/*/locations/*/memberships/*`. 347 string name = 1 [ 348 (google.api.field_behavior) = REQUIRED, 349 (google.api.resource_reference) = { 350 type: "gkehub.googleapis.com/Membership" 351 } 352 ]; 353 354 // Required. Mask of fields to update. 355 google.protobuf.FieldMask update_mask = 2 356 [(google.api.field_behavior) = REQUIRED]; 357 358 // Required. Only fields specified in update_mask are updated. 359 // If you specify a field in the update_mask but don't specify its value here 360 // that field will be deleted. 361 // If you are updating a map field, set the value of a key to null or empty 362 // string to delete the key from the map. It's not possible to update a key's 363 // value to the empty string. 364 // If you specify the update_mask to be a special path "*", fully replaces all 365 // user-modifiable fields to match `resource`. 366 Membership resource = 3 [(google.api.field_behavior) = REQUIRED]; 367 368 // Optional. A request ID to identify requests. Specify a unique request ID 369 // so that if you must retry your request, the server will know to ignore 370 // the request if it has already been completed. The server will guarantee 371 // that for at least 60 minutes after the first request. 372 // 373 // For example, consider a situation where you make an initial request and 374 // the request times out. If you make the request again with the same request 375 // ID, the server can check if original operation with the same request ID 376 // was received, and if so, will ignore the second request. This prevents 377 // clients from accidentally creating duplicate commitments. 378 // 379 // The request ID must be a valid UUID with the exception that zero UUID is 380 // not supported (00000000-0000-0000-0000-000000000000). 381 string request_id = 4 [(google.api.field_behavior) = OPTIONAL]; 382} 383 384// Request message for `GkeHub.GenerateConnectManifest` 385// method. 386// . 387message GenerateConnectManifestRequest { 388 // Required. The Membership resource name the Agent will associate with, in 389 // the format `projects/*/locations/*/memberships/*`. 390 string name = 1 [ 391 (google.api.field_behavior) = REQUIRED, 392 (google.api.resource_reference) = { 393 type: "gkehub.googleapis.com/Membership" 394 } 395 ]; 396 397 // Optional. Namespace for GKE Connect agent resources. Defaults to 398 // `gke-connect`. 399 // 400 // The Connect Agent is authorized automatically when run in the default 401 // namespace. Otherwise, explicit authorization must be granted with an 402 // additional IAM binding. 403 string namespace = 2 [(google.api.field_behavior) = OPTIONAL]; 404 405 // Optional. URI of a proxy if connectivity from the agent to 406 // gkeconnect.googleapis.com requires the use of a proxy. Format must be in 407 // the form `http(s)://{proxy_address}`, depending on the HTTP/HTTPS protocol 408 // supported by the proxy. This will direct the connect agent's outbound 409 // traffic through a HTTP(S) proxy. 410 bytes proxy = 3 [(google.api.field_behavior) = OPTIONAL]; 411 412 // Optional. The Connect agent version to use. Defaults to the most current 413 // version. 414 string version = 4 [(google.api.field_behavior) = OPTIONAL]; 415 416 // Optional. If true, generate the resources for upgrade only. Some resources 417 // generated only for installation (e.g. secrets) will be excluded. 418 bool is_upgrade = 5 [(google.api.field_behavior) = OPTIONAL]; 419 420 // Optional. The registry to fetch the connect agent image from. Defaults to 421 // gcr.io/gkeconnect. 422 string registry = 6 [(google.api.field_behavior) = OPTIONAL]; 423 424 // Optional. The image pull secret content for the registry, if not public. 425 bytes image_pull_secret_content = 7 [(google.api.field_behavior) = OPTIONAL]; 426} 427 428// GenerateConnectManifestResponse contains manifest information for 429// installing/upgrading a Connect agent. 430message GenerateConnectManifestResponse { 431 // The ordered list of Kubernetes resources that need to be applied to the 432 // cluster for GKE Connect agent installation/upgrade. 433 repeated ConnectAgentResource manifest = 1; 434} 435 436// ConnectAgentResource represents a Kubernetes resource manifest for Connect 437// Agent deployment. 438message ConnectAgentResource { 439 // Kubernetes type of the resource. 440 TypeMeta type = 1; 441 442 // YAML manifest of the resource. 443 string manifest = 2; 444} 445 446// TypeMeta is the type information needed for content unmarshalling of 447// Kubernetes resources in the manifest. 448message TypeMeta { 449 // Kind of the resource (e.g. Deployment). 450 string kind = 1; 451 452 // APIVersion of the resource (e.g. v1). 453 string api_version = 2; 454} 455 456// Request message for `GkeHub.ListFeatures` method. 457message ListFeaturesRequest { 458 // Required. The parent (project and location) where the Features will be listed. 459 // Specified in the format `projects/*/locations/*`. 460 string parent = 1 [ 461 (google.api.resource_reference) = { 462 child_type: "gkehub.googleapis.com/Feature" 463 } 464 ]; 465 466 // When requesting a 'page' of resources, `page_size` specifies number of 467 // resources to return. If unspecified or set to 0, all resources will 468 // be returned. 469 int32 page_size = 2; 470 471 // Token returned by previous call to `ListFeatures` which 472 // specifies the position in the list from where to continue listing the 473 // resources. 474 string page_token = 3; 475 476 // Lists Features that match the filter expression, following the syntax 477 // outlined in https://google.aip.dev/160. 478 // 479 // Examples: 480 // 481 // - Feature with the name "servicemesh" in project "foo-proj": 482 // 483 // name = "projects/foo-proj/locations/global/features/servicemesh" 484 // 485 // - Features that have a label called `foo`: 486 // 487 // labels.foo:* 488 // 489 // - Features that have a label called `foo` whose value is `bar`: 490 // 491 // labels.foo = bar 492 string filter = 4; 493 494 // One or more fields to compare and use to sort the output. 495 // See https://google.aip.dev/132#ordering. 496 string order_by = 5; 497} 498 499// Response message for the `GkeHub.ListFeatures` method. 500message ListFeaturesResponse { 501 // The list of matching Features 502 repeated Feature resources = 1; 503 504 // A token to request the next page of resources from the 505 // `ListFeatures` method. The value of an empty string means 506 // that there are no more resources to return. 507 string next_page_token = 2; 508} 509 510// Request message for `GkeHub.GetFeature` method. 511message GetFeatureRequest { 512 // Required. The Feature resource name in the format 513 // `projects/*/locations/*/features/*` 514 string name = 1 [ 515 (google.api.resource_reference) = { 516 type: "gkehub.googleapis.com/Feature" 517 } 518 ]; 519} 520 521// Request message for the `GkeHub.CreateFeature` method. 522message CreateFeatureRequest { 523 // Required. The parent (project and location) where the Feature will be created. 524 // Specified in the format `projects/*/locations/*`. 525 string parent = 1 [ 526 (google.api.resource_reference) = { 527 child_type: "gkehub.googleapis.com/Feature" 528 } 529 ]; 530 531 // The ID of the feature to create. 532 string feature_id = 2; 533 534 // The Feature resource to create. 535 Feature resource = 3; 536 537 // Optional. A request ID to identify requests. Specify a unique request ID 538 // so that if you must retry your request, the server will know to ignore 539 // the request if it has already been completed. The server will guarantee 540 // that for at least 60 minutes after the first request. 541 // 542 // For example, consider a situation where you make an initial request and 543 // the request times out. If you make the request again with the same request 544 // ID, the server can check if original operation with the same request ID 545 // was received, and if so, will ignore the second request. This prevents 546 // clients from accidentally creating duplicate commitments. 547 // 548 // The request ID must be a valid UUID with the exception that zero UUID is 549 // not supported (00000000-0000-0000-0000-000000000000). 550 string request_id = 4; 551} 552 553// Request message for `GkeHub.DeleteFeature` method. 554message DeleteFeatureRequest { 555 // Required. The Feature resource name in the format 556 // `projects/*/locations/*/features/*`. 557 string name = 1 [ 558 (google.api.resource_reference) = { 559 type: "gkehub.googleapis.com/Feature" 560 } 561 ]; 562 563 // If set to true, the delete will ignore any outstanding resources for 564 // this Feature (that is, `FeatureState.has_resources` is set to true). These 565 // resources will NOT be cleaned up or modified in any way. 566 bool force = 2; 567 568 // Optional. A request ID to identify requests. Specify a unique request ID 569 // so that if you must retry your request, the server will know to ignore 570 // the request if it has already been completed. The server will guarantee 571 // that for at least 60 minutes after the first request. 572 // 573 // For example, consider a situation where you make an initial request and 574 // the request times out. If you make the request again with the same request 575 // ID, the server can check if original operation with the same request ID 576 // was received, and if so, will ignore the second request. This prevents 577 // clients from accidentally creating duplicate commitments. 578 // 579 // The request ID must be a valid UUID with the exception that zero UUID is 580 // not supported (00000000-0000-0000-0000-000000000000). 581 string request_id = 3 [(google.api.field_behavior) = OPTIONAL]; 582} 583 584// Request message for `GkeHub.UpdateFeature` method. 585message UpdateFeatureRequest { 586 // Required. The Feature resource name in the format 587 // `projects/*/locations/*/features/*`. 588 string name = 1 [ 589 (google.api.resource_reference) = { 590 type: "gkehub.googleapis.com/Feature" 591 } 592 ]; 593 594 // Mask of fields to update. 595 google.protobuf.FieldMask update_mask = 2; 596 597 // Only fields specified in update_mask are updated. 598 // If you specify a field in the update_mask but don't specify its value here 599 // that field will be deleted. 600 // If you are updating a map field, set the value of a key to null or empty 601 // string to delete the key from the map. It's not possible to update a key's 602 // value to the empty string. 603 // If you specify the update_mask to be a special path "*", fully replaces all 604 // user-modifiable fields to match `resource`. 605 Feature resource = 3; 606 607 // Optional. A request ID to identify requests. Specify a unique request ID 608 // so that if you must retry your request, the server will know to ignore 609 // the request if it has already been completed. The server will guarantee 610 // that for at least 60 minutes after the first request. 611 // 612 // For example, consider a situation where you make an initial request and 613 // the request times out. If you make the request again with the same request 614 // ID, the server can check if original operation with the same request ID 615 // was received, and if so, will ignore the second request. This prevents 616 // clients from accidentally creating duplicate commitments. 617 // 618 // The request ID must be a valid UUID with the exception that zero UUID is 619 // not supported (00000000-0000-0000-0000-000000000000). 620 string request_id = 4; 621} 622 623// Represents the metadata of the long-running operation. 624message OperationMetadata { 625 // Output only. The time the operation was created. 626 google.protobuf.Timestamp create_time = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; 627 628 // Output only. The time the operation finished running. 629 google.protobuf.Timestamp end_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; 630 631 // Output only. Server-defined resource path for the target of the operation. 632 string target = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; 633 634 // Output only. Name of the verb executed by the operation. 635 string verb = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; 636 637 // Output only. Human-readable status of the operation, if any. 638 string status_detail = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; 639 640 // Output only. Identifies whether the user has requested cancellation 641 // of the operation. Operations that have successfully been cancelled 642 // have [Operation.error][] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, 643 // corresponding to `Code.CANCELLED`. 644 bool cancel_requested = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; 645 646 // Output only. API version used to start the operation. 647 string api_version = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; 648} 649