From 9d3e64cdafc302d7718bfa1b23358373478e79b8 Mon Sep 17 00:00:00 2001 From: Nathan McKinley Date: Wed, 24 Jan 2018 13:03:57 -0800 Subject: [PATCH] Add cloud endpoints resource (#933) OpenAPI & gRPC Endpoints on Compute Engine. New Resource: - Endpoints Service Create/Read/Delete - Example terraform config --- .../endpoints-on-compute-engine/.gitignore | 3 + examples/endpoints-on-compute-engine/main.tf | 136 ++++++++ .../scripts/install-vm.sh | 14 + .../endpoints-on-compute-engine/variables.tf | 20 ++ google/provider.go | 1 + google/resource_endpoints_service.go | 302 ++++++++++++++++++ google/resource_endpoints_service_test.go | 124 +++++++ google/resource_google_project_services.go | 4 +- google/serviceman_operation.go | 11 +- google/test-fixtures/test_api_descriptor.pb | Bin 0 -> 8752 bytes .../docs/r/endpoints_service.html.markdown | 63 ++++ website/google.erb | 9 + 12 files changed, 680 insertions(+), 7 deletions(-) create mode 100644 examples/endpoints-on-compute-engine/.gitignore create mode 100644 examples/endpoints-on-compute-engine/main.tf create mode 100644 examples/endpoints-on-compute-engine/scripts/install-vm.sh create mode 100644 examples/endpoints-on-compute-engine/variables.tf create mode 100644 google/resource_endpoints_service.go create mode 100644 google/resource_endpoints_service_test.go create mode 100644 google/test-fixtures/test_api_descriptor.pb create mode 100644 website/docs/r/endpoints_service.html.markdown diff --git a/examples/endpoints-on-compute-engine/.gitignore b/examples/endpoints-on-compute-engine/.gitignore new file mode 100644 index 00000000..16791642 --- /dev/null +++ b/examples/endpoints-on-compute-engine/.gitignore @@ -0,0 +1,3 @@ +terraform.tfstate +terraform.tfstate.backup +terraform.tfvars diff --git a/examples/endpoints-on-compute-engine/main.tf b/examples/endpoints-on-compute-engine/main.tf new file mode 100644 index 00000000..efeece8a --- /dev/null +++ b/examples/endpoints-on-compute-engine/main.tf @@ -0,0 +1,136 @@ +provider "google" { + region = "${var.region}" + credentials = "${file("${var.credentials_file_path}")}" +} + +provider "random" {} + +resource "random_id" "project_name" { + byte_length = 8 +} + +resource "google_project" "endpoints_project" { + name = "Endpoints Project" + project_id = "tf-ep-${random_id.project_name.hex}" + org_id = "${var.org_id}" + billing_account = "${var.billing_account_id}" +} + +resource "google_project_service" "endpoints_project" { + project = "${google_project.endpoints_project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_project_service" "endpoints_project_sm" { + project = "${google_project.endpoints_project.project_id}" + service = "servicemanagement.googleapis.com" +} + +resource "google_endpoints_service" "endpoints_service" { + service_name = "echo-api.endpoints.${google_project.endpoints_project.project_id}.cloud.goog" + project = "${google_project.endpoints_project.project_id}" + + config_text = < /tmp/goget_log.txt > /tmp/goget_outlog.txt +echo "deb http://packages.cloud.google.com/apt google-cloud-endpoints-jessie main" | sudo tee /etc/apt/sources.list.d/google-cloud-endpoints.list +curl --silent https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - +sudo apt-get update && sudo apt-get install google-cloud-sdk +sudo apt-get install endpoints-runtime +sudo echo "PORT=80" >> /etc/default/nginx +PORT=8081 GOPATH=$PWD GOROOT=$PWD/go go/bin/go run app.go > /tmp/gorun_outlog.txt 2> /tmp/gorun_log.txt & +sleep 10 +sudo service nginx restart diff --git a/examples/endpoints-on-compute-engine/variables.tf b/examples/endpoints-on-compute-engine/variables.tf new file mode 100644 index 00000000..e8c5bed0 --- /dev/null +++ b/examples/endpoints-on-compute-engine/variables.tf @@ -0,0 +1,20 @@ +variable "region" { + default = "us-central1" +} + +variable "region_zone" { + default = "us-central1-f" +} + +variable "org_id" { + description = "The ID of the Google Cloud Organization." +} + +variable "billing_account_id" { + description = "The ID of the associated billing account (optional)." +} + +variable "credentials_file_path" { + description = "Location of the credentials to use." + default = "~/.gcloud/Terraform.json" +} diff --git a/google/provider.go b/google/provider.go index e2cb5d25..d40b8018 100644 --- a/google/provider.go +++ b/google/provider.go @@ -139,6 +139,7 @@ func Provider() terraform.ResourceProvider { "google_dataproc_job": resourceDataprocJob(), "google_dns_managed_zone": resourceDnsManagedZone(), "google_dns_record_set": resourceDnsRecordSet(), + "google_endpoints_service": resourceEndpointsService(), "google_folder": resourceGoogleFolder(), "google_folder_iam_policy": ResourceIamPolicyWithImport(IamFolderSchema, NewFolderIamUpdater, FolderIdParseFunc), "google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(), diff --git a/google/resource_endpoints_service.go b/google/resource_endpoints_service.go new file mode 100644 index 00000000..358b87f8 --- /dev/null +++ b/google/resource_endpoints_service.go @@ -0,0 +1,302 @@ +package google + +import ( + "encoding/base64" + "encoding/json" + "errors" + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/servicemanagement/v1" +) + +func resourceEndpointsService() *schema.Resource { + return &schema.Resource{ + Create: resourceEndpointsServiceCreate, + Read: resourceEndpointsServiceRead, + Delete: resourceEndpointsServiceDelete, + Update: resourceEndpointsServiceUpdate, + Schema: map[string]*schema.Schema{ + "service_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "openapi_config": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"grpc_config", "protoc_output"}, + }, + "grpc_config": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "protoc_output": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "project": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "config_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "apis": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "syntax": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "version": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "methods": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "syntax": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "request_type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "response_type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "dns_address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "endpoints": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func getOpenAPIConfigSource(configText string) servicemanagement.ConfigSource { + // We need to provide a ConfigSource object to the API whenever submitting a + // new config. A ConfigSource contains a ConfigFile which contains the b64 + // encoded contents of the file. OpenAPI requires only one file. + configfile := servicemanagement.ConfigFile{ + FileContents: base64.StdEncoding.EncodeToString([]byte(configText)), + FileType: "OPEN_API_YAML", + FilePath: "heredoc.yaml", + } + return servicemanagement.ConfigSource{ + Files: []*servicemanagement.ConfigFile{&configfile}, + } +} + +func getGRPCConfigSource(serviceConfig, protoConfig string) servicemanagement.ConfigSource { + // gRPC requires both the file specifying the service and the compiled protobuf, + // but they can be in any order. + ymlConfigfile := servicemanagement.ConfigFile{ + FileContents: base64.StdEncoding.EncodeToString([]byte(serviceConfig)), + FileType: "SERVICE_CONFIG_YAML", + FilePath: "heredoc.yaml", + } + protoConfigfile := servicemanagement.ConfigFile{ + FileContents: base64.StdEncoding.EncodeToString([]byte(protoConfig)), + FileType: "FILE_DESCRIPTOR_SET_PROTO", + FilePath: "api_def.pb", + } + return servicemanagement.ConfigSource{ + Files: []*servicemanagement.ConfigFile{&ymlConfigfile, &protoConfigfile}, + } +} + +func resourceEndpointsServiceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + project, err := getProject(d, config) + if err != nil { + return err + } + // If the service doesn't exist, we'll need to create it, but if it does, it + // will be reused. This is unusual for Terraform, but it causes the behavior + // that users will want and accept. Users of Endpoints are not thinking in + // terms of services, configs, and rollouts - they just want the setup declared + // in their config to happen. The fact that a service may need to be created + // is not interesting to them. Consequently, we create this service if necessary + // so that we can perform the rollout without further disruption, which is the + // action that a user running `terraform apply` is going to want. + serviceName := d.Get("service_name").(string) + servicesService := servicemanagement.NewServicesService(config.clientServiceMan) + _, err = servicesService.Get(serviceName).Do() + if err != nil { + _, err = servicesService.Create(&servicemanagement.ManagedService{ProducerProjectId: project, ServiceName: serviceName}).Do() + if err != nil { + return err + } + } + // Do a rollout using the update mechanism. + err = resourceEndpointsServiceUpdate(d, meta) + if err != nil { + return err + } + + d.SetId(serviceName) + return resourceEndpointsServiceRead(d, meta) +} + +func resourceEndpointsServiceUpdate(d *schema.ResourceData, meta interface{}) error { + // This update is not quite standard for a terraform resource. Instead of + // using the go client library to send an HTTP request to update something + // serverside, we have to push a new configuration, wait for it to be + // parsed and loaded, then create and push a rollout and wait for that + // rollout to be completed. + // There's a lot of moving parts there, and all of them have knobs that can + // be tweaked if the user is using gcloud. In the interest of simplicity, + // we currently only support full rollouts - anyone trying to do incremental + // rollouts or A/B testing is going to need a more precise tool than this resource. + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + var source servicemanagement.ConfigSource + if openapiConfig, ok := d.GetOk("openapi_config"); ok { + source = getOpenAPIConfigSource(openapiConfig.(string)) + } else { + grpcConfig, gok := d.GetOk("grpc_config") + protocOutput, pok := d.GetOk("protoc_output") + if gok && pok { + source = getGRPCConfigSource(grpcConfig.(string), protocOutput.(string)) + } else { + return errors.New("Could not decypher config - please either set openapi_config or set both grpc_config and protoc_output.") + } + } + + configService := servicemanagement.NewServicesConfigsService(config.clientServiceMan) + // The difference between "submit" and "create" is that submit parses the config + // you provide, where "create" requires the config in a pre-parsed format. + // "submit" will be a lot more flexible for users and will always be up-to-date + // with any new features that arise - this is why you provide a YAML config + // instead of providing the config in HCL. + op, err := configService.Submit(serviceName, &servicemanagement.SubmitConfigSourceRequest{ConfigSource: &source}).Do() + if err != nil { + return err + } + s, err := serviceManagementOperationWait(config, op, "Submitting service config.") + if err != nil { + return err + } + var serviceConfig servicemanagement.SubmitConfigSourceResponse + json.Unmarshal(s, &serviceConfig) + + // Next, we create a new rollout with the new config value, and wait for it to complete. + rolloutService := servicemanagement.NewServicesRolloutsService(config.clientServiceMan) + rollout := servicemanagement.Rollout{ + ServiceName: serviceName, + TrafficPercentStrategy: &servicemanagement.TrafficPercentStrategy{ + Percentages: map[string]float64{serviceConfig.ServiceConfig.Id: 100.0}, + }, + } + op, err = rolloutService.Create(serviceName, &rollout).Do() + if err != nil { + return err + } + _, err = serviceManagementOperationWait(config, op, "Performing service rollout.") + if err != nil { + return err + } + return resourceEndpointsServiceRead(d, meta) +} + +func resourceEndpointsServiceDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + servicesService := servicemanagement.NewServicesService(config.clientServiceMan) + op, err := servicesService.Delete(d.Get("service_name").(string)).Do() + if err != nil { + return err + } + _, err = serviceManagementOperationWait(config, op, "Deleting service.") + d.SetId("") + return err +} + +func resourceEndpointsServiceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + servicesService := servicemanagement.NewServicesService(config.clientServiceMan) + service, err := servicesService.GetConfig(d.Get("service_name").(string)).Do() + if err != nil { + return err + } + d.Set("config_id", service.Id) + d.Set("dns_address", service.Name) + d.Set("apis", flattenServiceManagementAPIs(service.Apis)) + d.Set("endpoints", flattenServiceManagementEndpoints(service.Endpoints)) + + return nil +} + +func flattenServiceManagementAPIs(apis []*servicemanagement.Api) []map[string]interface{} { + flattened := make([]map[string]interface{}, len(apis)) + for i, a := range apis { + flattened[i] = map[string]interface{}{ + "name": a.Name, + "version": a.Version, + "syntax": a.Syntax, + "methods": flattenServiceManagementMethods(a.Methods), + } + } + return flattened +} + +func flattenServiceManagementMethods(methods []*servicemanagement.Method) []map[string]interface{} { + flattened := make([]map[string]interface{}, len(methods)) + for i, m := range methods { + flattened[i] = map[string]interface{}{ + "name": m.Name, + "syntax": m.Syntax, + "request_type": m.RequestTypeUrl, + "response_type": m.ResponseTypeUrl, + } + } + return flattened +} + +func flattenServiceManagementEndpoints(endpoints []*servicemanagement.Endpoint) []map[string]interface{} { + flattened := make([]map[string]interface{}, len(endpoints)) + for i, e := range endpoints { + flattened[i] = map[string]interface{}{ + "name": e.Name, + "address": e.Target, + } + } + return flattened +} diff --git a/google/resource_endpoints_service_test.go b/google/resource_endpoints_service_test.go new file mode 100644 index 00000000..c48705d7 --- /dev/null +++ b/google/resource_endpoints_service_test.go @@ -0,0 +1,124 @@ +package google + +import ( + "testing" + + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/servicemanagement/v1" +) + +func TestAccEndpointsService_basic(t *testing.T) { + t.Parallel() + random_name := "t-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccEndpointsService_basic(random_name), + Check: testAccCheckEndpointExistsByName(random_name), + }, + }, + }) +} + +func TestAccEndpointsService_grpc(t *testing.T) { + t.Parallel() + random_name := "t-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccEndpointsService_grpc(random_name), + Check: testAccCheckEndpointExistsByName(random_name), + }, + }, + }) +} + +func testAccEndpointsService_basic(random_name string) string { + return fmt.Sprintf(`resource "google_endpoints_service" "endpoints_service" { + service_name = "%s.endpoints.%s.cloud.goog" + project = "%s" + openapi_config = <icfpdR6t_8}_#g?8=_& z?sc7|zUKz+&R}=RIp_y(3&PYV<)E}Mm`k%UgT_m*+4+`xP&hVTVq&yTSoQeN=Jz_m z{$QtonM-?a*Y52dxdBMu@ujza#^b-!66^KSAGO(aL8l-f;IXjfbh|(4xre@R0n{EVKMjp@sOI>W^WEw{@{gWX-n^Z6o|Q1d=-+kwqHy}#2B}ncE=CA&dwm{xIJ$7+I-+Upu>H4;I$m_%TA|fdv66V zKhF;#8+SeNUv~hW2X4Ew+iBT^kmt7NaCkoG1cB4$@cFva#vj4H9pGQIx^CA!?DY0{ z%k8y0^pQ^=0mljMgA)C7^B6IoySpKymfLpt!N3o&^1udDLbZ3?*A5VsVvP6Ppwn{l z80`4Gi!~C$ut%(Pln#Vwb?wf9<3Y)jNx<2V79k0&u|0rP5f64m~Kuz z-U)nKQBQ`6i|9SD-|`)YA_XG4-08LPD}X#Aao`4s0HH~M=mt9kC*Q>Y11@&McXxwB z3KJrV2;zRs}2R0N3qkBQRMpwkOIP@xnVYVqqf%EDq04|-*v0@%q$yaUfRZ> z!B=bbZOdHWX!4C(rEFLYUaXe!Yqe>arOjr|YA~KFHt=aqj1;Te+<0EMj7EdkEN(un zS4^M*-YQm`rqRfAv$|T@ESuH!JO=`=)|!l0%%^4(^P081aQoqRytc-l8rJFtV2UNP zVm7yh!!@&6C1-0ji}50_7p+e&yuNAGYYl_b%F1SAwNf;n8s!4~!zVYM8Pz6l zY!oY%(S`snIl>n@xkS*J@=UcEhlq znX5+Q0k70Z{x!bYFhHbSY!-!UU_dU6V*#bjhAD(It4+hQHtS8ZR=t5;-hwh%dJ*52 zg~GKet(IcNs9Dg4;S_lFVC0I7OhqF-0VJVTIpLBq$CjM(piO=Bt$YLB1|BXttNxa-1o04#QE4&g*c`s$dHr9 zfH2DDg)78`*p~`1r6 z>@542#a2Gu2|YhS-;1U z{*IDILOh5j=(F%)3>#jvaq zXNU?harm-#PFHUTB8<^oOy6Y_Lt&=hJ{@Cds@m=B=R`I9ykwa$`t-%qorLrLLl4_* zVkm4swUVK!p`VZF3i3TR9>uO+IGueoF@#oq$iR;b>;KhL3JW=>eM`YHyr&rIyY`XQ(dQ9#8&ym3kCY zvG3yS_hX_*F@+w*Z^lO24R`L1l!Vl6u&5K^qb?7-4Jd{1(&Bwa%b^u;N!azy8tq|ND(F(9d+qK`WKs*D zWFe~2F+p@-q9V0aY?uucMO$WbgQ|+7%2X9+Ws@o)Q9e=i@e@>cd9JE^=M7~jsNazz zk$|E<)No{%B6LrA3kek!;M4^hPy)Dzc8VvGJkiS$RXEx-J!E>_4thJH6X6eSe6%HO z>HqpqSo-LhYbqU{iBfjtv1G^$mr9?RC}odnG#I7qFp+kVttKNKo{3WS3$Z1YU}F*) zauSt37oxI4ls;t21Y4FqWUI+Y*)Pl%6vvCP#|jY`WXMTW`dl0>eaMuEvh*QaO$Oq{ zi$7-LOiN%fm($nSFPWB#QG;CYyQm zs3NFke-FudLk&`ghuUd7G(je-DN=1VGtC~cWJ*ZNH8Xn$mw?Qe?WnV6 zQstXJ_eE#{GCzNd-4c*EGzD!fJ3FL3krVyL57wOqY$0Nr25cc>ng(oPvBbU<1#@ya z_QQDgJe@h(m+1U}VOZ2HXU?*#ESVJNWJR0I&S3VS*Fj03KI))Hj|RXPO-bs>6>V8a zP9}6+(bBXcvOsGkBjS!M5Lr2Ujy(|A7`msO&x)Fw%DM2Zle8AlMU`#%zUu_uTRaCr z3vsEu#^Lpzwi0@c!|OdQqrB4Txi>E28(!(W+?&?fES4YFllNoAcvi=PgqY#&p;k{{ z-p`z291O)Z`4Foot<>tpH__rT12i6L_oXYCpz%;GUS6vY)#71+#zQRrx`2kCN7_vG z5-%c#MR^)7Uj-nE8`o%=kFNG%f1i)j6I~lGspz2ns z7uPhvKGq)12)$qe_HhJDx5Hzl9>xIs80y`EdYYar$JXQ7FA?QoL&HT16V=OTvUIyj zN`fh8uCN905v{Q`?fcm)(H(vA)k-sQ?AJ6IGO%#4)+WRWq%mNwO^eRzFUg2Yz111GAvNGK$*lH z)059)+aD8msQMaZO}FPWh`R{ZqD}Z5toezop>A8WDHK&q2i51=GpR02P<^hb${yo$ zMHLpPJ_psipo)Uv+t@GScyCXNgQ%VxB}#Ylx0y=-NG3)1@db#^@#E4S?sr=I;Zx^| zghA28N1*#c`&MLSgaNuQ6kXZCe4*&V0^JviZdy<7#2iUiov5gBDNj~OBsr>4D1(;n z`kl;WHV3+C4GC3S%VsYNUFq^brYzGI5fETVF^G9MS8a8!U?E!D6Y}nW1;qB$g>WVa zwl7^q0;44}dU8K@5YH~0sOM3+Ix1)ec>7@Z4H#yK;VTGw`^23GSE*}s!0@&W-GTTV z(!SF6MS4nTkoJ|578X3cQqsZ#^H-1-T_ViHP*{mI^IGO;3at9Cpu5vOAD!Tu=66gSg07KD>fd-94()IKcXui>wH~K>v3jL{g z9~T-0nkO}a2}eQXh#sZ@uP6l`#^5NJoD$*0^yC}V>mLcHqt3yoObmE$5Kcb^RYo$s zgG|fEsLJbaR3>?+jEV#Ri1bc-BLfO1xW7}}VZqlsB@!%fe+QB1eP9$&?;(_evs+!Oi>0sj!p&Z|c-o0{{R3 literal 0 HcmV?d00001 diff --git a/website/docs/r/endpoints_service.html.markdown b/website/docs/r/endpoints_service.html.markdown new file mode 100644 index 00000000..504cc9d5 --- /dev/null +++ b/website/docs/r/endpoints_service.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "google" +page_title: "Google: google_endpoints_service" +sidebar_current: "docs-google-endpoints-service" +description: |- + Creates and rolls out a Google Endpoints service. +--- + +# google_endpoints_service + +This resource creates and rolls out a Cloud Endpoints service using OpenAPI or gRPC. View the relevant docs for [OpenAPI](https://cloud.google.com/endpoints/docs/openapi/) and [gRPC](https://cloud.google.com/endpoints/docs/grpc/). + +## Example Usage + +```hcl +resource "google_endpoints_service" "openapi_service" { + service_name = "api-name.endpoints.project-id.cloud.goog" + project = "project-id" + openapi_config = "${file("openapi_spec.yml")}" +} + +resource "google_endpoints_service" "grpc_service" { + service_name = "api-name.endpoints.project-id.cloud.goog" + project = "project-id" + grpc_config = "${file("service_spec.yml")}" + protoc_output = "${file("compiled_descriptor_file.pb")}" +} +``` + +The example in `examples/endpoints_on_compute_engine` shows the API from the quickstart running on a Compute Engine VM and reachable through Cloud Endpoints, which may also be useful. + +## Argument Reference + +The following arguments are supported: +* `service_name`: (Required) The name of the service. Usually of the form `$apiname.endpoints.$projectid.cloud.goog`. +* `openapi_config`: (Optional) The full text of the OpenAPI YAML configuration as described [here](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md). Either this, or *both* of `grpc_config` and `protoc_output` must be specified. +* `grpc_config`: (Optional) The full text of the Service Config YAML file (Example located [here](https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/bookstore-grpc/api_config.yaml)). If provided, must also provide `protoc_output`. `open_api` config must *not* be provided. +* `protoc_output`: (Optional) The full contents of the Service Descriptor File generated by protoc. This should be a compiled .pb file. +* `project`: (Optional) The project ID that the service belongs to. If not provided, provider project is used. + +## Attributes Reference +In addition to the arguments, the following attributes are available: +* `config_id`: The autogenerated ID for the configuration that is rolled out as part of the creation of this resource. Must be provided to compute engine instances as a tag. +* `dns_address`: The address at which the service can be found - usually the same as the service name. +* `apis`: A list of API objects; structure is documented below. +* `endpoints`: A list of Endpoint objects; structure is documented below. + +- - - +### API Object Structure +* `name`: The FQDN of the API as described in the provided config. +* `syntax`: `SYNTAX_PROTO2` or `SYNTAX_PROTO3`. +* `version`: A version string for this api. If specified, will have the form major-version.minor-version, e.g. `1.10`. +* `methods`: A list of Method objects; structure is documented below. + +### Method Object Structure +* `name`: The simple name of this method as described in the provided config. +* `syntax`: `SYNTAX_PROTO2` or `SYNTAX_PROTO3`. +* `request_type`: The type URL for the request to this API. +* `response_type`: The type URL for the response from this API. + +### Endpoint Object Structure +* `name`: The simple name of the endpoint as described in the config. +* `address`: The FQDN of the endpoint as described in the config. diff --git a/website/google.erb b/website/google.erb index cac9e7c9..3a28109b 100644 --- a/website/google.erb +++ b/website/google.erb @@ -396,6 +396,15 @@ + > + Google Endpoints Resources + + + > Google PubSub Resources