mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-05 17:52:38 +00:00
Merge pull request #2147 from terraform-providers/paddy_new_appengine
Add google_app_engine_application resource.
This commit is contained in:
commit
69589e5465
|
@ -107,6 +107,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
GeneratedRedisResourcesMap,
|
GeneratedRedisResourcesMap,
|
||||||
GeneratedResourceManagerResourcesMap,
|
GeneratedResourceManagerResourcesMap,
|
||||||
map[string]*schema.Resource{
|
map[string]*schema.Resource{
|
||||||
|
"google_app_engine_application": resourceAppEngineApplication(),
|
||||||
"google_bigquery_dataset": resourceBigQueryDataset(),
|
"google_bigquery_dataset": resourceBigQueryDataset(),
|
||||||
"google_bigquery_table": resourceBigQueryTable(),
|
"google_bigquery_table": resourceBigQueryTable(),
|
||||||
"google_bigtable_instance": resourceBigtableInstance(),
|
"google_bigtable_instance": resourceBigtableInstance(),
|
||||||
|
|
285
google/resource_app_engine_application.go
Normal file
285
google/resource_app_engine_application.go
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/customdiff"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform/helper/validation"
|
||||||
|
appengine "google.golang.org/api/appengine/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAppEngineApplication() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAppEngineApplicationCreate,
|
||||||
|
Read: resourceAppEngineApplicationRead,
|
||||||
|
Update: resourceAppEngineApplicationUpdate,
|
||||||
|
Delete: resourceAppEngineApplicationDelete,
|
||||||
|
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: schema.ImportStatePassthrough,
|
||||||
|
},
|
||||||
|
|
||||||
|
CustomizeDiff: customdiff.All(
|
||||||
|
appEngineApplicationLocationIDCustomizeDiff,
|
||||||
|
),
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"project": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateProjectID(),
|
||||||
|
},
|
||||||
|
"auth_domain": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"location_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.StringInSlice([]string{
|
||||||
|
"northamerica-northeast1",
|
||||||
|
"us-central",
|
||||||
|
"us-west2",
|
||||||
|
"us-east1",
|
||||||
|
"us-east4",
|
||||||
|
"southamerica-east1",
|
||||||
|
"europe-west",
|
||||||
|
"europe-west2",
|
||||||
|
"europe-west3",
|
||||||
|
"asia-northeast1",
|
||||||
|
"asia-south1",
|
||||||
|
"australia-southeast1",
|
||||||
|
}, false),
|
||||||
|
},
|
||||||
|
"serving_status": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: validation.StringInSlice([]string{
|
||||||
|
"UNSPECIFIED",
|
||||||
|
"SERVING",
|
||||||
|
"USER_DISABLED",
|
||||||
|
"SYSTEM_DISABLED",
|
||||||
|
}, false),
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"feature_settings": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
MaxItems: 1,
|
||||||
|
Elem: appEngineApplicationFeatureSettingsResource(),
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"url_dispatch_rule": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: appEngineApplicationURLDispatchRuleResource(),
|
||||||
|
},
|
||||||
|
"code_bucket": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"default_hostname": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"default_bucket": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"gcr_domain": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appEngineApplicationURLDispatchRuleResource() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"domain": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"service": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appEngineApplicationFeatureSettingsResource() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"split_health_checks": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appEngineApplicationLocationIDCustomizeDiff(d *schema.ResourceDiff, meta interface{}) error {
|
||||||
|
old, new := d.GetChange("location_id")
|
||||||
|
if old != "" && old != new {
|
||||||
|
return fmt.Errorf("Cannot change location_id once the resource is created.")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAppEngineApplicationCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
project, err := getProject(d, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
app, err := expandAppEngineApplication(d, project)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Creating App Engine App")
|
||||||
|
op, err := config.clientAppEngine.Apps.Create(app).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating App Engine application: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(project)
|
||||||
|
|
||||||
|
// Wait for the operation to complete
|
||||||
|
waitErr := appEngineOperationWait(config.clientAppEngine, op, project, "App Engine app to create")
|
||||||
|
if waitErr != nil {
|
||||||
|
d.SetId("")
|
||||||
|
return waitErr
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Created App Engine App")
|
||||||
|
|
||||||
|
return resourceAppEngineApplicationRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAppEngineApplicationRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
pid := d.Id()
|
||||||
|
|
||||||
|
app, err := config.clientAppEngine.Apps.Get(pid).Do()
|
||||||
|
if err != nil {
|
||||||
|
return handleNotFoundError(err, d, fmt.Sprintf("App Engine Application %q", pid))
|
||||||
|
}
|
||||||
|
d.Set("auth_domain", app.AuthDomain)
|
||||||
|
d.Set("code_bucket", app.CodeBucket)
|
||||||
|
d.Set("default_bucket", app.DefaultBucket)
|
||||||
|
d.Set("default_hostname", app.DefaultHostname)
|
||||||
|
d.Set("location_id", app.LocationId)
|
||||||
|
d.Set("name", app.Name)
|
||||||
|
d.Set("serving_status", app.ServingStatus)
|
||||||
|
d.Set("project", pid)
|
||||||
|
dispatchRules, err := flattenAppEngineApplicationDispatchRules(app.DispatchRules)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.Set("url_dispatch_rule", dispatchRules)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error setting dispatch rules in state. This is a bug, please report it at https://github.com/terraform-providers/terraform-provider-google/issues. Error is:\n%s", err.Error())
|
||||||
|
}
|
||||||
|
featureSettings, err := flattenAppEngineApplicationFeatureSettings(app.FeatureSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.Set("feature_settings", featureSettings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error setting feature settings in state. This is a bug, please report it at https://github.com/terraform-providers/terraform-provider-google/issues. Error is:\n%s", err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAppEngineApplicationUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
pid := d.Id()
|
||||||
|
app, err := expandAppEngineApplication(d, pid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Updating App Engine App")
|
||||||
|
op, err := config.clientAppEngine.Apps.Patch(pid, app).UpdateMask("authDomain,servingStatus,featureSettings.splitHealthChecks").Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error updating App Engine application: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the operation to complete
|
||||||
|
waitErr := appEngineOperationWait(config.clientAppEngine, op, pid, "App Engine app to update")
|
||||||
|
if waitErr != nil {
|
||||||
|
return waitErr
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Updated App Engine App")
|
||||||
|
|
||||||
|
return resourceAppEngineApplicationRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAppEngineApplicationDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
log.Println("[WARN] App Engine applications cannot be destroyed once created. The project must be deleted to delete the application.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandAppEngineApplication(d *schema.ResourceData, project string) (*appengine.Application, error) {
|
||||||
|
result := &appengine.Application{
|
||||||
|
AuthDomain: d.Get("auth_domain").(string),
|
||||||
|
LocationId: d.Get("location_id").(string),
|
||||||
|
Id: project,
|
||||||
|
GcrDomain: d.Get("gcr_domain").(string),
|
||||||
|
ServingStatus: d.Get("serving_status").(string),
|
||||||
|
}
|
||||||
|
featureSettings, err := expandAppEngineApplicationFeatureSettings(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.FeatureSettings = featureSettings
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandAppEngineApplicationFeatureSettings(d *schema.ResourceData) (*appengine.FeatureSettings, error) {
|
||||||
|
blocks := d.Get("feature_settings").([]interface{})
|
||||||
|
if len(blocks) < 1 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &appengine.FeatureSettings{
|
||||||
|
SplitHealthChecks: d.Get("feature_settings.0.split_health_checks").(bool),
|
||||||
|
// force send SplitHealthChecks, so if it's set to false it still gets disabled
|
||||||
|
ForceSendFields: []string{"SplitHealthChecks"},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenAppEngineApplicationFeatureSettings(settings *appengine.FeatureSettings) ([]map[string]interface{}, error) {
|
||||||
|
if settings == nil {
|
||||||
|
return []map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
result := map[string]interface{}{
|
||||||
|
"split_health_checks": settings.SplitHealthChecks,
|
||||||
|
}
|
||||||
|
return []map[string]interface{}{result}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenAppEngineApplicationDispatchRules(rules []*appengine.UrlDispatchRule) ([]map[string]interface{}, error) {
|
||||||
|
results := make([]map[string]interface{}, 0, len(rules))
|
||||||
|
for _, rule := range rules {
|
||||||
|
results = append(results, map[string]interface{}{
|
||||||
|
"domain": rule.Domain,
|
||||||
|
"path": rule.Path,
|
||||||
|
"service": rule.Service,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
77
google/resource_app_engine_application_test.go
Normal file
77
google/resource_app_engine_application_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAppEngineApplication_basic(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
org := getTestOrgFromEnv(t)
|
||||||
|
pid := acctest.RandomWithPrefix("tf-test")
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccAppEngineApplication_basic(pid, org),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttrSet("google_app_engine_application.acceptance", "url_dispatch_rule.#"),
|
||||||
|
resource.TestCheckResourceAttrSet("google_app_engine_application.acceptance", "name"),
|
||||||
|
resource.TestCheckResourceAttrSet("google_app_engine_application.acceptance", "code_bucket"),
|
||||||
|
resource.TestCheckResourceAttrSet("google_app_engine_application.acceptance", "default_hostname"),
|
||||||
|
resource.TestCheckResourceAttrSet("google_app_engine_application.acceptance", "default_bucket"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ResourceName: "google_app_engine_application.acceptance",
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccAppEngineApplication_update(pid, org),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ResourceName: "google_app_engine_application.acceptance",
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAppEngineApplication_basic(pid, org string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_project" "acceptance" {
|
||||||
|
project_id = "%s"
|
||||||
|
name = "%s"
|
||||||
|
org_id = "%s"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_app_engine_application" "acceptance" {
|
||||||
|
project = "${google_project.acceptance.project_id}"
|
||||||
|
auth_domain = "hashicorptest.com"
|
||||||
|
location_id = "us-central"
|
||||||
|
serving_status = "SERVING"
|
||||||
|
}`, pid, pid, org)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAppEngineApplication_update(pid, org string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_project" "acceptance" {
|
||||||
|
project_id = "%s"
|
||||||
|
name = "%s"
|
||||||
|
org_id = "%s"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_app_engine_application" "acceptance" {
|
||||||
|
project = "${google_project.acceptance.project_id}"
|
||||||
|
auth_domain = "tf-test.club"
|
||||||
|
location_id = "us-central"
|
||||||
|
serving_status = "USER_DISABLED"
|
||||||
|
}`, pid, pid, org)
|
||||||
|
}
|
|
@ -92,10 +92,12 @@ func resourceGoogleProject() *schema.Resource {
|
||||||
Set: schema.HashString,
|
Set: schema.HashString,
|
||||||
},
|
},
|
||||||
"app_engine": &schema.Schema{
|
"app_engine": &schema.Schema{
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: appEngineResource(),
|
Computed: true,
|
||||||
MaxItems: 1,
|
Elem: appEngineResource(),
|
||||||
|
MaxItems: 1,
|
||||||
|
Deprecated: "Use the google_app_engine_application resource instead.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -206,13 +208,8 @@ func appEngineFeatureSettingsResource() *schema.Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceGoogleProjectCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error {
|
func resourceGoogleProjectCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error {
|
||||||
if old, new := diff.GetChange("app_engine.#"); old != nil && new != nil && old.(int) > 0 && new.(int) < 1 {
|
if old, _ := diff.GetChange("app_engine.0.location_id"); diff.HasChange("app_engine.0.location_id") && old != nil && old.(string) != "" {
|
||||||
// if we're going from app_engine set to unset, we need to delete the project, app_engine has no delete
|
return fmt.Errorf("Cannot change app_engine.0.location_id once the app is created.")
|
||||||
return diff.ForceNew("app_engine")
|
|
||||||
} else if old, _ := diff.GetChange("app_engine.0.location_id"); diff.HasChange("app_engine.0.location_id") && old != nil && old.(string) != "" {
|
|
||||||
// if location_id was already set, and has a new value, that forces a new app
|
|
||||||
// if location_id wasn't set, don't force a new value, as we're just enabling app engine
|
|
||||||
return diff.ForceNew("app_engine.0.location_id")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
72
website/docs/r/app_engine_application.html.markdown
Executable file
72
website/docs/r/app_engine_application.html.markdown
Executable file
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
layout: "google"
|
||||||
|
page_title: "Google: google_app_engine_application"
|
||||||
|
sidebar_current: "docs-google-app-engine-application"
|
||||||
|
description: |-
|
||||||
|
Allows management of an App Engine application.
|
||||||
|
---
|
||||||
|
|
||||||
|
# google_app_engine_application
|
||||||
|
|
||||||
|
Allows creation and management of an App Engine application.
|
||||||
|
|
||||||
|
~> App Engine applications cannot be deleted once they're created; you have to delete the
|
||||||
|
entire project to delete the application. Terraform will report the application has been
|
||||||
|
successfully deleted; this is a limitation of Terraform, and will go away in the future.
|
||||||
|
Terraform is not able to delete App Engine applications.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "google_project" "my_project" {
|
||||||
|
name = "My Project"
|
||||||
|
project_id = "your-project-id"
|
||||||
|
org_id = "1234567"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_app_engine_application" "app" {
|
||||||
|
project = "${google_project.my_project.project_id}"
|
||||||
|
location_id = "us-central'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `location_id` - (Required) The [location](https://cloud.google.com/appengine/docs/locations)
|
||||||
|
to serve the app from.
|
||||||
|
|
||||||
|
* `auth_domain` - (Optional) The domain to authenticate users with when using App Engine's User API.
|
||||||
|
|
||||||
|
* `serving_status` - (Optional) The serving status of the app.
|
||||||
|
|
||||||
|
* `feature_settings` - (Optional) A block of optional settings to configure specific App Engine features:
|
||||||
|
|
||||||
|
* `split_health_checks` - (Optional) Set to false to use the legacy health check instead of the readiness
|
||||||
|
and liveness checks.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
In addition to the arguments listed above, the following computed attributes are
|
||||||
|
exported:
|
||||||
|
|
||||||
|
* `name` - Unique name of the app, usually `apps/{PROJECT_ID}`
|
||||||
|
|
||||||
|
* `url_dispatch_rule` - A list of dispatch rule blocks. Each block has a `domain`, `path`, and `service` field.
|
||||||
|
|
||||||
|
* `code_bucket` - The GCS bucket code is being stored in for this app.
|
||||||
|
|
||||||
|
* `default_hostname` - The default hostname for this app.
|
||||||
|
|
||||||
|
* `default_bucket` - The GCS bucket content is being stored in for this app.
|
||||||
|
|
||||||
|
* `gcr_domain` - The GCR domain used for storing managed Docker images for this app.
|
||||||
|
|
||||||
|
## Import
|
||||||
|
|
||||||
|
Applications can be imported using the ID of the project the application belongs to, e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform import google_app_engine_application.app your-project-id
|
||||||
|
```
|
|
@ -132,6 +132,15 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-google-app-engine") %>>
|
||||||
|
<a href="#">Google App Engine Resources</a>
|
||||||
|
<ul class="nav nav-visible">
|
||||||
|
<li<%= sidebar_current("docs-google-app-engine-application") %>>
|
||||||
|
<a href="/docs/providers/google/r/app_engine_application.html">google_app_engine_application</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-google-bigquery") %>>
|
<li<%= sidebar_current("docs-google-bigquery") %>>
|
||||||
<a href="#">Google BigQuery Resources</a>
|
<a href="#">Google BigQuery Resources</a>
|
||||||
<ul class="nav nav-visible">
|
<ul class="nav nav-visible">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user