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

Define the Galley API #122

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions galley/v1/service.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// Copyright 2017 Istio Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package istio.galley.v1;

import "google/api/annotations.proto";
import "google/protobuf/struct.proto";
import "google/rpc/status.proto";

// Galley follows
// the Kubenertes API server delegation and resource model.
Copy link

Choose a reason for hiding this comment

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

the Galley design seems to abandon the k8s approach:
A granular resource oriented API based on the Kubernetes resource model was considered here. However it does not provide the required CI/CD experience therefore this document proposes a hierarchical resource API.
Can this be clarified?

// /apiGroup/version/objecttype/{namespace}/objectname
//
// It generalizes the concept of namespace to an arbitrary grouping.
//
// /apiGroup/objecttype/version/{objectGroup}/objectname
Copy link
Contributor

Choose a reason for hiding this comment

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

consistent with camel case

//
// apiGroup = /core/
// apiGroup = /vendor1/
service Galley {
// Get a single object.
rpc GetObject(GetObjectRequest) returns (ConfigObject) {
option (google.api.http) = {
get: "/{key.api_group}/{key.object_type}/{key.object_type_version}/{key.object_group}/{key.name}"
};
};

// Get a list of objects.
// Other arguments like query.object_group, query.name are optional
// This lets the caller list all objects of a type, or all objects of a type
// that is within an object group.
rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse) {
option (google.api.http) = {
// meta.object_group can be specified as a query parameter
get: "/{query.api_group}/{query.object_type}/{query.object_type_version}"
additional_bindings: {
get: "/{query.api_group}/{query.object_type}/{query.object_type_version}/{query.object_group}"
};
};
};

// Get a list of object types.
// Lists all object types or all object types
// within an api group.
rpc ListObjectTypes(ListObjectTypesRequest) returns (ListObjectTypesResponse) {
option (google.api.http) = {
get: "/{meta.api_group}"
Copy link

Choose a reason for hiding this comment

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

nit: meta.api_group -> query.api_group

additional_bindings: {
get: "/"
Copy link

Choose a reason for hiding this comment

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

As I commented previously, grpc-gateway can't compile with this spec (see grpc-ecosystem/grpc-gateway#414). Consider removing this additional binding for the time being.

};
};
};

// Create an object. This may result in configuration validation errors.
// Referential integrity should be maximally enforced, except where not possible or desirable.
Copy link

Choose a reason for hiding this comment

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

I'm not sure what this line means. Could you elaborate more?

rpc CreateObject(UpdateObjectRequest) returns (UpdateObjectResponse) {
option (google.api.http) = {
post: "/{key.api_group}/{key.object_type}/{key.object_type_version}/{key.object_group}"
body: "source_data"
};
};

// Update a single object. May return validation errors.
rpc UpdateObject(UpdateObjectRequest) returns (UpdateObjectResponse) {
option (google.api.http) = {
put: "/{key.api_group}/{key.object_type}/{key.object_type_version}/{key.object_group}/{key.name}"
body: "source_data"
};
};

// Delete a single object. May return validation errors.
Copy link
Contributor

Choose a reason for hiding this comment

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

So it's possible that Delete will fail due to consistency constraints. That's always difficult for the user. The errors we return will need to include the steps needed to be able to delete.

rpc DeleteObject(DeleteObjectRequest) returns (DeleteObjectResponse) {
option (google.api.http) = {
delete: "/{meta.api_group}/{meta.object_type}/{meta.object_type_version}/{meta.object_group}/{meta.name}"
Copy link

Choose a reason for hiding this comment

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

nit: meta -> key

};
};
};


// DeleteObjectResponse is returned when an attempt is made to delete an object
// from the store.
message DeleteObjectResponse{
// result of the operation
// if status == INVALID_ARGUMENT
// check errors for details.
google.rpc.Status status = 1;

repeated ValidationError validation_errors = 2;
}

// UpdateObjectResponse is returned when an attempt is made to update the store.
// ADD or UPDATE operations.
message UpdateObjectResponse {
// result of the operation
// if status == INVALID_ARGUMENT
// check errors for details.
google.rpc.Status status = 1;

// state of the object after the change.
ConfigObject object = 2;

// errors if st
repeated ValidationError validation_errors = 3;
}

message ValidationError {
// The key of the object where error was detected.
// This may not be the object that introduced the error.
string key = 1;
// The primary field that was in error, if available.
string field = 2;
// The line number on which error occurred, if available.
int32 line_number = 3;
Copy link

Choose a reason for hiding this comment

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

Since the config object has no information about the original source (source_data is already converted into Struct proto), it's probably impossible to respond with line number in the original source. Maybe we should keep the original source string in the config object too?

// Textual error message.
string error = 4;
}

message ListObjectTypesRequest {
// meta is the metadata associated with the root where listing begins.
Meta query = 1;

// paged result, set to empty on first page.
string page_token = 2;

// If non zero, response should have at most these number of entries.
int32 max_page_size = 3;
}

message ListObjectsRequest {
// meta is the metadata associated with the root where listing begins.
Meta query = 1;

// paged result, set to empty on first page.
string page_token = 2;

// If non zero, response should have at most these number of entries.
int32 max_page_size = 3;
}

message UpdateObjectRequest {
// key associated with the requested object.
Meta key = 1;

// oneof source_data or data

// source_data is the data as it was specified in json / yaml format.
google.protobuf.Struct source_data = 2;

// data is the binary encoded protobuf data
// For example:
// If `source_data` contains an expressions in text form
// `data` may contain a parsed AST
bytes data = 3;
}

message GetObjectRequest {
// key associated with the object to fetch.
Meta key = 1;
}

message DeleteObjectRequest {
// key associated with the requested object.
Meta key = 1;
Copy link

@elevran elevran Jul 11, 2017

Choose a reason for hiding this comment

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

any validation that the meta.version, meta.revision, etc. matches the current state before deletion?

}

// ConfigObject is the concrete representation of a resource.
message ConfigObject {
// meta is the metadata associated with the object.
Meta meta = 1;

// source_data is the data as it was specified in json / yaml format.
google.protobuf.Struct source_data = 2;

// data is the binary encoded protobuf data
Copy link
Contributor

Choose a reason for hiding this comment

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

The comment here and on ObjectRequest.data probably should match.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

// For example:
// If `source_data` contains an expressions in text form
// `data` may contain a parsed AST
bytes data = 3;
}

message ListObjectTypesResponse {
// meta is the metadata associated with this list.
Meta meta = 1;

// list of objects returned.
repeated Meta object_types = 2;

// If next_page_token is not empty, this is a paged result.
// use this value in the next request.
string next_page_token = 3;
}

message ListObjectsResponse {
// meta is the metadata associated with this list.
Meta meta = 1;

// list of objects returned.
repeated ConfigObject objects = 2;

// If next_page_token is not empty, this is a paged result.
// use this value in the next request.
string next_page_token = 3;
}


// Meta is the metadata associated with an object.
message Meta {
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 want a payload_type enum as suggested yesterday in our conversation with Daniel?

// api_group is the top level delegation and extension point.
string api_group = 1;

// object_type is the type of an object.
// It corresponds to a behavior being configured.
// route-rule, metrics, logs, ...
string object_type = 2;

// every object type is independently versioned.
string object_type_version = 3;

// object_group is an arbitrary hierarchical grouping of resources.
// For Mixer configuration object_group == mesh, svc:svc1, svc:svc2 ...
// For Proxy it can be mesh, destination.
string object_group = 4;
Copy link

Choose a reason for hiding this comment

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

the comment above seems to suggest that string could have a format (e.g., svc:svc1, etc.) that indicates the grouping.
Is this the intent? Is Galley aware of the grouping and/or syntax used?


// name of the object.
string name = 5;

// uuid assigned to the object. Only used when a new object is created
// in the same place as before.
string uuid = 6;

// revision of the repository, the last time ConfigObject was updated.
int64 revision = 7;

// labels associated with the object.
map<string, string> labels = 8;
}

89 changes: 89 additions & 0 deletions galley/v1/validator.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2017 Istio Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package istio.galley.v1;

import "google/rpc/status.proto";
import "google/api/annotations.proto";

import "service.proto";
Copy link

Choose a reason for hiding this comment

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

nit: import line should specify the path from the root. i.e. galley/v1/service.proto


// ValidatorAndTransformer service validates objects before they are committed to storage.
// Galley maintains a map of object_types to validators. A single validator may validate many types.
// For example MixerValidator service should validate all Mixer resources.
service ValidatorAndTransformer {
// Validate the resource and convert it to typed proto if applicable
// if Object.source_data is specified, it should be converted
// to the appropriate proto representation.
// Every attempt should be made to do a deep validation.
// If full validation requires referential integrity checks, this service should use the
// GalleyWatch Service to maintain current view of configuration.
//
// For example a Mixer rule consists of a selector expression and a named handler amongst other things.
// Mixer validator should check
// 1. expression syntax is valid
// 2. expression uses known attributes
// 3. Rule refers to a known handler
// 4. Any other semantic check that is required for full validation.
//
// It should convert untyped proto into typed proto and return binary encoding of it in Object.data.
//
// On validation failure, it should return a validation error with text.
rpc ValidateAndTransform(ValidationRequest) returns (ValidationResponse) {
option (google.api.http) = {
post: "/resources/v1:validate"
body: "*"
};
};
};

// Validation repesents individual change to be validated.
message Validation {
oneof validation_union {
// This object should be validated and converted to binary encoded proto.
ConfigObject object = 1;

// If this object was deleted.
// In this case Validator should check referential integrity.
// Only metadata about the object being deleted is sent.
Meta deleted = 2;
Copy link

Choose a reason for hiding this comment

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

And deny deletion if it could result in loss of referential integrity?
What about cross-component referential integrity - needed, possible?

}
}

message ValidationRequest {
// Supports multiple ordered changes to be validated together.
// When a large config is pushed into Galley, batching
// validation requests is an optimization.
repeated Validation validations = 1;
// Objects should be validated against this revision of the repository.
// If the validator or the object is agnostic to repository versions, this can be ignored.
// This is useful for referential integrity checking.
int64 validation_revision = 3;
}

message ValidationResponse {
// result of the operation
// if status == INVALID_ARGUMENT
// check errors for details.
google.rpc.Status status = 1;

// state of the object after the change.
ConfigObject object = 2;

// errors if status = INVALID_ARGUMENT
repeated ValidationError validation_errors = 3;
}


Loading