Skip to content

Add native openapi, grpc, and rest apis support to Istio #368

New issue

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

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

Already on GitHub? # to your account

Closed
wants to merge 16 commits into from

Conversation

ayj
Copy link
Contributor

@ayj ayj commented Feb 9, 2018

Background

Istio API spec and attribute generation proposed how to configure an API in the Istio proxyto generate API related attributes. The result was the mixerclient api_spec.proto which is necessary but not sufficient to satisfy API mgmt support in Istio. At a minimum, users should author intent-based feature-level config without regard for which component(s) implement it (ref #344, https://goo.gl/QQPLdL).

The new types included in this PR are just a strawman to help with a discussion. It's mostly copy/pasted from the mixerclient spec with API keys removes and openapi/grpc added.

Motivations

Proposal

Create a new API group for describing APIs and how they map to service(s) in Istio, e.g. apis.istio.io/<version>. New type(s) are created in this group to describe APIs using OpenAPI (v2/v3), gRPC, and generic http verb/path pattern matching schemes.

Some components may only want to consume the normalized generic HTTP form as its defined today for mixerclient. A mutating webhook can be transform high-level schemes such as OpenAPI/GRPC to the normalized form and store it along side the original spec before the resource is submitted to storage and advertised to components.

Once defined, Pilot would start consuming this new API(s) to create the mixerclient filter configuration. Additional features could be added later, e.g. transcoding, request valdiation, tracing.

open questions (non-exhaustive):

  1. Should Istio openapi be natively support openapi? Should it reside exclusively outside of Istio, requiring client-side tooling to translate into normalized http scheme?

  2. If "yes" to Add structure for istioapi in a new repo #1, should we support a single union type, unique type per scheme/protocol, or some combination in between, e.g.

    1. single API union type of all supported schemes across all protocols
    2. one API union type per protocol, e.g. HTTPAPI includes OpenAPI, gRPC, HTTP.
    3. one type per scheme, e.g. OpenAPIv2, OpenAPIv3, GRPC, GenericHTTP
  3. How should API(s) be bound to services? Inline via repeated destinations field or separate binding resource?

@googlebot googlebot added the cla: yes Set by the Google CLA bot to indicate the author of a PR has signed the Google CLA. label Feb 9, 2018
@ayj
Copy link
Contributor Author

ayj commented Feb 9, 2018

OpenAPIv2 openapiv2 = 2;
OpenAPIv3 openapiv3 = 3;
Grpc grpc = 4;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. http_generic is low level and internal. opanapi and grpc are user facing, high level, they should not be mixed.

  2. Here openapi only for generating api attributes, or it may generate other config, such as auth or security, or api-key requirement

Copy link
Contributor

Choose a reason for hiding this comment

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

Is HTTP generic more general than OpenAPI or less general?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alternate name suggestions welcome :)

I'd like to retain the option for a user to define a basic REST API without needing to restore to OpenAPI. cc @mpnally

Copy link
Contributor Author

@ayj ayj Feb 9, 2018

Choose a reason for hiding this comment

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

In general, I think we want security definitions to be ignored in this context. AFAIK, the current standard schema is not sufficient anyway to configure Istio authentication, e.g. JWT validation. api_key may be a special case since it's defined as a security definition but not covered by authentication policy. As an exception, we could add ApiKey submessage back to the generic HttpApi message and extract that from OpenAPI during normalization.

message OpenApiv2 {
oneof data {
// utf-8 encoding of OpenAPI v2 document written in YAML or JSON.
string yaml = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Use google.protobuf.Struct for YAML or JSON fields?

It will keep the data structured without lose any information.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some downstream tools for conversion, normalization, validation, and analysis expect YAML/JSON input, e.g. https://github.com/googleapis/gnostic, https://godoc.org/github.com/go-openapi/validate. We could convert back to JSON/YAML from google.protobuf.Struct internally but we risk losing providence.

message Grpc {
oneof data {
// Binary content of the proto descriptor set for the gRPC service.
bytes binary = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Note the name after oneof doesn't appear in field name, so you would better to name this as descriptor_set_binary. ditto for above.

Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of oneof constraint here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need so much structure? Is this actually any better than

message ApiSpec {
string type; // or enum
bytes data;
}

There is no type safety with all these messages anyway.

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 rather have strongly discriminated types, since in addition to the raw data for a given format, we can also include additional metadata by adding new fields to the message.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need to wrap an external schema as a CRD? I have seen an API spec that is over 20MB, which can not fit into CRD. Another issue is many API spec is auto generated from other build rules, not written as CRD yaml file. It would be much better to refer to the API spec as a reference than embed it as bytes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Inlined and externally referenced schemas are both viable models. Something like envoy API's DataSource may work here to allow for multiple schema sources? Validation would be required for inline case to avoid size issues. The external reference could be an http path or even service name to enable server reflection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For example,

message SchemaSource {
  oneof specifier {
    // Remote reference to schema. 
    string external_reference = 1;

    // Bytes inlined in the configuration. 
    bytes inline_bytes = 2;

    // String inlined in the configuration.
    string inline_string = 3;
  }
}

GRPC would likely use inline_bytes for small-ish services or an external reference. OpenAPI could use inline_string (via multi-line yaml), inline_bytes (base64 encoded?), or external reference, e.g.

spec:
  openapiv2:
    externalReference: https://storage.googleapis.com/ayj0/petstore.json

Non-HTTP external references are another option if stronger ACLs are preferred, e.g.

spec:
  openapiv2:
    externalReference: gs://ayj0/petstore.json

// Binary content of the proto descriptor set for the gRPC service.
bytes binary = 1;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It is better to add a field repeated string services to specify service names explicitly. It is common that a descriptor set have multiple services, due to dependencies, but only a subset of them are served by a backend.


package istio.apis.v1alpha1;

option go_package="istio.io/api/apis/v1alpha1";
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: api/apis feels like stutter. Maybe use IDL - standing for interface description, instead, e.g. api/idl.

Copy link
Contributor

Choose a reason for hiding this comment

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

IDL isn't appropriate for this package IMO.

To avoid the direct studder, perhaps istio.apimanagement.v1alpha1 would be better?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

apimanagement might be too broad when we consider versioning. The IDL may reach beta fairly soon given we're referencing existing schema. But other features not may be developed for some time. If they're in the same group we either need to "hold back" the apimanagement group to v1alpha or let end up needing to add wip features to a beta/stable group.

Alternatively, we add more granular groups and use idl here and add a separate group(s) later for the other features.

message OpenApiv2 {
oneof data {
// utf-8 encoding of OpenAPI v2 document written in YAML or JSON.
string yaml = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

message Grpc {
oneof data {
// Binary content of the proto descriptor set for the gRPC service.
bytes binary = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of oneof constraint here?

//
// istioctl create httpapi petstore --type=openapiv2 --from-file=petstore-simple.yaml
//
message HTTPAPI {
Copy link
Contributor

Choose a reason for hiding this comment

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

HttpApi, or perhaps something better :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Http probably ought to be in the name since we might, in theory, support other protocols in the future. HttpIdl or HttpInterfaceDefinition?

OpenAPIv2 openapiv2 = 2;
OpenAPIv3 openapiv3 = 3;
Grpc grpc = 4;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is HTTP generic more general than OpenAPI or less general?

@@ -0,0 +1,197 @@
// Copyright 2017 Istio Authors
Copy link
Contributor

Choose a reason for hiding this comment

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

wrong year

message Grpc {
oneof data {
// Binary content of the proto descriptor set for the gRPC service.
bytes binary = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need so much structure? Is this actually any better than

message ApiSpec {
string type; // or enum
bytes data;
}

There is no type safety with all these messages anyway.

// Optional namespace of the API. Defaults to the encompassing
// HTTPAPIBinding's metadata namespace field.
string namespace = 2;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we want to define a reference message for each type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely not. I'd much rather define the destination binding inline in the top-level type. That does mean some potential duplicate if an API is shared across services, but that is partly mitigated with the proposed external_reference.

// REQUIRED. One or more API references that should be mapped to
// the specified service(s). The aggregate collection of match
// conditions defined in the APIs should not overlap.
repeated HTTPAPIReference apis = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this different from generic Istio routing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure I understand the question. Are you asking why APIs aren't defined/bound by existing routing APIs?

string http_method = 2;

oneof pattern {
// URI template to match against as defined by
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you really promise of the full spec?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@ayj ayj Feb 10, 2018

Choose a reason for hiding this comment

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

It looks like we ought to restrict support to a much smaller subset based on OAI/OpenAPI-Specification#1459. But it's not clear to me what wording or reference I should include here to specify the semantics. Is "path templating matching as defined by OAS 3.0.1" with a reference to the spec sufficient, or is additional clarification needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

// name: petstore
// spec:
// openapiv2: |
// swagger: "2.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

How does k8s handle these type of foreign configs? Can we just store the Swagger file somewhere directly? How do you plan to store the proto descriptor here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ConfigMaps store data inline, e.g. https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#create-configmaps-from-directories.

Secrets also store data inline. Binary data is typically basd64 encoded.

base64 encoding may be okay for smallish grpc services and demos where external file store is not desirable. We could add support for external_reference for all other cases. See the SchemeSource example below.

Copy link
Contributor

@geeknoid geeknoid left a comment

Choose a reason for hiding this comment

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

I love the general approach of having CRDs for each of these concepts. I think it's clean and fits very nicely with the rest of our concepts.


package istio.apis.v1alpha1;

option go_package="istio.io/api/apis/v1alpha1";
Copy link
Contributor

Choose a reason for hiding this comment

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

IDL isn't appropriate for this package IMO.

To avoid the direct studder, perhaps istio.apimanagement.v1alpha1 would be better?

option (gogoproto.gostring_all) = false;

// OpenAPI v2 specification
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
Copy link
Contributor

Choose a reason for hiding this comment

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

Can make this a real markdown link.

Copy link
Contributor Author

@ayj ayj Feb 9, 2018

Choose a reason for hiding this comment

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

Yup. This all needs better documentation with examples before its merged.

message OpenApiv3 {
oneof data {
// utf-8 encoding of OpenAPI v3 document written in YAML or JSON.
string yaml = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

For all of these fields, I think we want a name representing the semantic of the data rather than its format.

message Grpc {
oneof data {
// Binary content of the proto descriptor set for the gRPC service.
bytes binary = 1;
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 rather have strongly discriminated types, since in addition to the raw data for a given format, we can also include additional metadata by adding new fields to the message.

@wora
Copy link
Contributor

wora commented Feb 11, 2018

We should ask at least someone from Kubernetes community about how to represent foreign specifications. In order to represent Google APIs, such as Compute Engine API, the API spec can be more than 10MB.

If we are building an infrastructure, we must be able to handle at least 100MB. While the number sounds silly, but that is what the reality is. People like to pull in unnecessary dependencies and create bloated APIs.

@ayj
Copy link
Contributor Author

ayj commented Feb 12, 2018

Kubernetes volumes may be another option for managing large (> 1mb) API specs. Our API spec CRD would need a reference to the backing volume and sub-path within that volume that holds the API spec document(s).

@erictune, @lavalamp - any additional recommendations on how Istio might be able to manage API spec that exceed CRD size limitations?

@erictune
Copy link

What size are we talking about?

@ayj
Copy link
Contributor Author

ayj commented Feb 12, 2018

At least 100Mb per @wora's comments. Many Google API's are at least 10Mb (e.g. Compute Engine API).

@lavalamp
Copy link

The entire Kubernetes API spec is 3.5 MB. What is going in the spec that is making it so big?

Kubernetes Volumes are not going to work as an overflow system (apiserver has no way to retrieve data from a volume). I do not see the 1MB limit being relaxed in the near future.

Compression? Omit documentation?

@kyessenov
Copy link
Contributor

kyessenov commented Feb 12, 2018 via email

@lavalamp
Copy link

lavalamp commented Feb 12, 2018 via email

@ayj
Copy link
Contributor Author

ayj commented Feb 12, 2018

@lavalamp, I think you might be referring to CRD validation with OpenAPI v3 schema? I don't expect the schema of Istio CRD themselves to exceed the 1mb limitation.

This issue is referring to existing HTTP-based API services (e.g. gRPC service) we'd like to add to Istio mesh with API-level policy/telemetry support. Asking a user to refactor their API so it can be supported by Istio is likely not an option in the long term.

Pruning types/documentation is suitable for simple mapping of method/path to API operation (see proposed HTTPGeneric).

UX features requires documentation, e.g. developer portals. We could store the API documentation elsewhere, but we'd need to retain a reference inline for discoverability.

For the purposes of transcoding and request-level validation, pruning unused types/services from proto descriptor sets might help, but I'm not sure if it we get us under the 1mb limit for extremely large services, e.g. some Google APIs.

// could be hidden from the user. Alternatively, it might be useful
// to reflect this back into the user-facing configuration resource
// to aid in debugability.
HTTPGeneric normalized_http = 6;
Copy link
Contributor

@frankbu frankbu Feb 12, 2018

Choose a reason for hiding this comment

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

Is this the canonical api definition that includes all the information that Istio needs for its attribute generation? If so, why do we ever need to in-line the other often-bloated representations? Wouldn't it suffice to just import the other formats (with a reference back to the source), and only storing the generic form in the CRD, e.g., import swagger using istioctl create httpapi ...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

HTTPGeneric is sufficient for API attribute generation. Transcoding, request-level validation, enhanced tracing, and UX discovery require additional information. "reference back to the source" is one valid option and is what @wora suggested here. We'd want to standardize on what a valid "source" is so that tooling can consistently reason about it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'm still missing something. Doesn't the additional information required by transcoding, request-level validation, etc., need to be converted into a single normalized format used at runtime? Otherwise we need multiple implementations of these functions, don't we? In other words, I was thinking that HTTPGeneric could include ALL the required information, and therefore all we would need to store in the CRD.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not necessarily. Envoy's gRPC-JSON transcoder needs the proto descriptor set. Some developer portals expect the full OpenAPI/gRPC spec. HTTPGeneric as it is today isn't sufficient. We'd need to extend it and may end up with something similar to https://github.com/googleapis/googleapis/blob/master/google/api/service.proto (minus all of the backend, policy, billing, etc. fields).

@lavalamp
Copy link

@ayj Thanks, sorry-- yes, I thought this was talking about the schema to be sent with the CRD. I agree that Istio probably ought to handle anything out there and I don't have a strong opinion about where such things should be stored, other than that in a CR or elsewhere in the control plane isn't really an option, at least not in the short term :(

@bgrant0607
Copy link

Are the content-type-specific API payload schemas even useful at this level, or just potentially used by policy engine plugins?

I'm not only concerned about the size, but also about how many different formats and versions are supported, how clear it is what subset of each is supported, scope creep into the API management space, and so on.

What can be done to keep this layer minimalistic and extensible?

@wora
Copy link
Contributor

wora commented Feb 13, 2018

Creating a superset spec to represent other specs would be a huge effort. We tried once, e.g. google.protobuf.Api, and it didn't go anywhere. If we only care about OpenAPI v2/v3 and gRPC for now, we can just implement two dedicated filters to handle them. The cost of duplication would be much less than creating another spec. With an extra spec, we also need 2 converters, which are not any simpler than 2 Envoy filters.

We need to handle very large APIs. While we don't like them, but that is that the reality is. If Istio cannot handle Compute or YouTube, it would be difficult to be a real platform.

@ayj
Copy link
Contributor Author

ayj commented Feb 13, 2018

This API spec only describes the API surface (e.g. methods, paths, parameters, docs). Policy (auth, quota, etc) are defined by other configuration, though they may reference the attributes defined by the API spec (e.g. api.operation).

I think we only care about OpenAPI v2/v3, gRPC, and the generic HTTP form. I haven't heard anyone ask for anything else yet. We definitely ought to be conservative about introducing additional schema/versions without a strong justification.

rshriram and others added 8 commits February 13, 2018 16:20
* Update STYLE-GUIDE.md

Mention the CRD kind must match proto message.

* Update STYLE-GUIDE.md

Mention `apiVersion` must match proto package.

* Update STYLE-GUIDE.md

Explain apiVersion = group/version.
Update guideline not to use acronyms in API definition. Fixed istio#364.
Add a rule for repeated fields.
* Address comment

* Address comment

* Fix typo
* Doc clarifications

* regenerate go files
@googlebot
Copy link
Collaborator

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this State. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

@googlebot googlebot added cla: no Set by the Google CLA bot to indicate the author of a PR has not signed the Google CLA. and removed cla: yes Set by the Google CLA bot to indicate the author of a PR has signed the Google CLA. labels Feb 14, 2018
@ayj ayj changed the title wip: strawman proposal for adding native openapi, grpc, and rest apis support to Istio Add native openapi, grpc, and rest apis support to Istio Feb 14, 2018
// Grpc, and HttpGeneric in the `apis.istio.io/v1alpha1` apiversion.
google.protobuf.Struct schema = 2;

// List of destination services that this API is bound
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rshriram, @ZackButcher - the definition and usage of gateways and destinations here probably aren't 100% correct, but the intent was to align with the concepts defined by routing APIs. What I'd like to do is describe an HTTP API and bind that API to a service, subset of services (e.g. new version of a service has a new API), and/or standalone edge proxies (e.g. ingress). This would translate into Pilot delivering the API spec as part of an envoy filter / route. I'm hoping the conceptual alignment will make it easier to integrate with the ongoing Pilot feature enhancements.

Copy link
Member

Choose a reason for hiding this comment

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

Are you trying to pin the API spec to a proxy here, wherein this applies in the inbound path?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's one use case, e.g. mixerclient needs IDL to produce API operation attribute based on HTTP verb/path.

@smawson
Copy link
Contributor

smawson commented Feb 14, 2018

Hey is there a doc or issue describing the proposal in a little more detail? I have a hard time visualizing how the pieces are intended to fit together in the context of a single proto like this. I'd much prefer commenting in a doc sent to the api management working group.

I'd like to see this built in a pluggable way, so additional api descriptor languages can be plugged in and translated to a smaller set of representations that go to the proxy. I don't think we can expect an additional schema to result in new code in pilot and proxy in addition to the crd as a reasonable model here. On the other hand the set of schemas may be small enough we just hard code a few and call it done, but I generally dislike those kinds of answers.

I also wouldn't bother with trying to support externalized schemas at all right now, and instead focus on a simple CRD model + a translation to a smaller internal representation that we pass down to the proxy which is offered as a library and as a hook. Then anyone with a super large schema can compile it themselves using the library. Otherwise you're asking for a lot of trouble trying to deal with external schemas, and Istio isn't authoritative, and it is going to hurt.

@ayj
Copy link
Contributor Author

ayj commented Feb 15, 2018

I'll close this issue for now and start a doc as @smawson suggested. We can discuss/review in the next WG meeting on 2/28.

@ayj ayj closed this Feb 15, 2018
incfly pushed a commit to incfly/api that referenced this pull request Jun 13, 2018
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
cla: no Set by the Google CLA bot to indicate the author of a PR has not signed the Google CLA.
Projects
None yet
Development

Successfully merging this pull request may close these issues.