Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cloud endpoints resource #933

Merged
merged 8 commits into from
Jan 24, 2018

Conversation

nat-henderson
Copy link
Contributor

If merged, this closes #714. The resource works like this:

  • You provide the config text, either as a heredoc or by loading a file.
  • Terraform creates the service if necessary.
  • Terraform creates a new configuration for the service based on what you've provided.
  • Terraform creates and waits for a new rollout for the service, converting 100% of traffic to the new config.
  • Terraform computes and provides API names and types, endpoint names, config version, service name, and DNS address for the service.

This also includes a simple test, and an example which brings up an echo-api running on Compute Engine as in https://cloud.google.com/endpoints/docs/openapi/get-started-compute-engine.

Worth noting: this only works for the OpenAPI spec - gRPC is not implemented here, and Endpoints Frameworks is app-engine only. If someone needs / wants that, please post here or on #714.

This should deploy any OpenAPI spec you provide, including one that works with App Engine Standard or Kubernetes.

New Resource:
- Endpoints Service Create/Read/Delete
- Example terraform config

To do:
- Endpoints Service Update
- Tests
@nat-henderson
Copy link
Contributor Author

Hold off on this review - @ean commented in #714 about another feature they need that I didn't put into this PR.

@nat-henderson nat-henderson removed the request for review from danawillow January 11, 2018 20:03
@nat-henderson nat-henderson self-assigned this Jan 11, 2018
@nat-henderson nat-henderson requested a review from rosbo January 12, 2018 18:17

# google_endpoints_service

This resource creates and rolls out a Cloud Endpoints service using OpenAPI. View the relevant docs [here](https://cloud.google.com/endpoints/docs/openapi/).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add also a or gRPC

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


This resource creates and rolls out a Cloud Endpoints service using OpenAPI. View the relevant docs [here](https://cloud.google.com/endpoints/docs/openapi/).

## Example Usage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider putting two examples, one for gRPC and one for open API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

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; details below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We often used Structure is documented below instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// 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)
openapi_config, ok := d.GetOk("openapi_config")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using camelcase for variable and method parameter names

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, you can see where I went and wrote some python between starting and finishing this method. So embarrassing - thanks. Is there a go linter I should run to catch this sort of thing?

Copy link
Contributor

@rosbo rosbo Jan 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/golang/lint

Unfortunately, there is a lot of noise because historically, we haven't been strict about it

},
"openapi_config": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a ConflictsWith: []string{"grpc_config", "protoc_output"}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I didn't know about that.

Type: schema.TypeString,
Optional: true,
},
"protoc_output": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I like the name protoc_output, seems like a computed field to me at first glance. Would protoc_config be a better name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's not a config - it's just a giant raw binary file which is output by protoc. I can rename it to, maybe, protoc_service_descriptor_bytes or something like that - thoughts?

"config_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
ForceNew: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is this ForceNew if it is only an exported field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that makes no sense. Removed - you can't get a new config_id unless the configs change, anyway.

Type: schema.TypeString,
Optional: true,
},
"grpc_config": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the content of the config file changes, does it create a diff? If not, it should

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, yep.

}
}
// Do a rollout using the update mechanism.
err = resourceEndpointsServiceUpdate(d, meta)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it fail instead if the resource already exists? Like in all our other resources, if the resource already exists, the user should call terraform import.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "resource" here is really the rollout, I think - the creation of the service object is incidental to the creation of the first rollout.

},
"config_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we tend to use the order Required -> Optional -> Computed for schema elements (and if something is both optional and computed it just counts as optional for this ordering)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}

func getServiceConfigSource(config_text string) servicemanagement.ConfigSource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/config_text/configText

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - and all the other ones.

}
serviceName := d.Get("service_name").(string)
servicesService := servicemanagement.NewServicesService(config.clientServiceMan)
_, err = servicesService.Get(serviceName).Do()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So usually in tf we would assume that if we made it to the Create fn then the resource doesn't exist in GCP, or if it does the Create call should error (and then the user can import the resource if it does already exist). If there is something that makes endpoints special that we should still allow create to be called if it does already exist, then let's comment why that is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Added comment.

}

metadata {
endpoints-service-name = "${google_endpoints_service.endpoints_service.service_name}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: alignment (terraform fmt in this directory should take care of it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}

func getGrpcConfigSource(service_config, proto_config string) servicemanagement.ConfigSource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! Okay, thanks.

serviceName := d.Get("service_name").(string)
openapi_config, ok := d.GetOk("openapi_config")
var source servicemanagement.ConfigSource
if ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could do if openapiConfig, ok := d.GetOk("openapi_config"); ok { here since that var is only used within the scope of the if/else

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field isn't settable, so it should be computed and not required (likewise for all the others in here and endpoints)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
d.Set("config_id", service.Id)
d.Set("dns_address", service.Name)
d.Set("apis", flattenServiceManagementApi(service.Apis))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: flattenServiceManagementApis, perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}

func TestAccEndpointsService_basic(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh, we normally put the capital-T test functions at the top of the file and the helpers below it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
```

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove "which may also be useful" since that's implied, but you're welcome to keep it if you want

@nat-henderson
Copy link
Contributor Author

Ping @danawillow and @rosbo - what else needs to be done? :)

@nat-henderson nat-henderson merged commit 9d3e64c into hashicorp:master Jan 24, 2018
chrisst pushed a commit to chrisst/terraform-provider-google that referenced this pull request Nov 9, 2018
OpenAPI & gRPC Endpoints on Compute Engine.

New Resource:
- Endpoints Service Create/Read/Delete
- Example terraform config
modular-magician pushed a commit to modular-magician/terraform-provider-google that referenced this pull request Sep 27, 2019
add support for port_specification to resource `google_compute_health_check`
@ghost
Copy link

ghost commented Mar 29, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks!

@ghost ghost locked and limited conversation to collaborators Mar 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: google_endpoints_endpoint resource
3 participants