Skip to content

Commit

Permalink
Add Content-Type header to all requests with bodies.
Browse files Browse the repository at this point in the history
All operations with requests bodies, except for patch requests, now add
a `Content-Type: application/json` header to the request.

The `Patch` type is now generated as an enum of `Json`, `Merge` and
`StrategicMerge` variants, each wrapping a `serde_json::Value`.

Patch operations use the variant of the `Patch` value to set the appropriate
`Content-Type` header.

This commit also fixes the response types of delete-collection operations to
contain the list type of the associated type, not the associated type itself.

Ref #43
Fixes #49
  • Loading branch information
Arnavion committed Jul 22, 2019
1 parent 9d55072 commit 36334e6
Show file tree
Hide file tree
Showing 787 changed files with 12,311 additions and 4,142 deletions.
81 changes: 70 additions & 11 deletions k8s-openapi-codegen-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub fn run<W>(
definitions: &std::collections::BTreeMap<swagger20::DefinitionPath, swagger20::Schema>,
operations: &mut Vec<swagger20::Operation>,
definition_path: &swagger20::DefinitionPath,
ref_path_relative_to: swagger20::RefPathRelativeTo,
replace_namespaces: &[(&[std::borrow::Cow<'static, str>], &[std::borrow::Cow<'static, str>])],
crate_root: &str,
mod_root: Option<&str>,
Expand All @@ -91,7 +92,7 @@ pub fn run<W>(

let type_ref_path = swagger20::RefPath {
path: definition_path.0.clone(),
relative_to: swagger20::RefPathRelativeTo::Scope,
relative_to: ref_path_relative_to,
can_be_default: None,
};

Expand Down Expand Up @@ -681,6 +682,27 @@ pub fn run<W>(
run_result.num_generated_structs += 1;
},

swagger20::SchemaKind::Ty(swagger20::Type::Patch) => {
writeln!(out, "#[derive(Clone, Debug, PartialEq)]")?;
writeln!(out, "{}enum {} {{", vis, type_name)?;
writeln!(out, " Json(Vec<serde_json::Value>),")?;
writeln!(out, " Merge(serde_json::Value),")?;
writeln!(out, " StrategicMerge(serde_json::Value),")?;
writeln!(out, "}}")?;
writeln!(out)?;
writeln!(out, "impl serde::Serialize for {} {{", type_name)?;
writeln!(out, " fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {{")?;
writeln!(out, " match self {{")?;
writeln!(out, r#" {}::Json(patch) => serializer.serialize_newtype_struct("{}", patch),"#, type_name, type_name)?;
writeln!(out, r#" {}::Merge(patch) |"#, type_name)?;
writeln!(out, r#" {}::StrategicMerge(patch) => serializer.serialize_newtype_struct("{}", patch),"#, type_name, type_name)?;
writeln!(out, " }}")?;
writeln!(out, " }}")?;
writeln!(out, "}}")?;

run_result.num_generated_structs += 1;
},

swagger20::SchemaKind::Ty(swagger20::Type::WatchEvent(raw_extension_ref_path)) => {
let has_bookmark_event_type = {
let watch_optional_schema =
Expand Down Expand Up @@ -1291,6 +1313,7 @@ fn get_rust_borrow_type(
swagger20::SchemaKind::Ty(swagger20::Type::JSONSchemaPropsOrArray) |
swagger20::SchemaKind::Ty(swagger20::Type::JSONSchemaPropsOrBool) |
swagger20::SchemaKind::Ty(swagger20::Type::JSONSchemaPropsOrStringArray) => Err("JSON schema types not supported".into()),
swagger20::SchemaKind::Ty(swagger20::Type::Patch) => Err("Patch type not supported".into()),

swagger20::SchemaKind::Ty(swagger20::Type::WatchEvent(_)) => Err("WatchEvent type not supported".into()),
swagger20::SchemaKind::Ty(swagger20::Type::DeleteOptional(_)) => Err("DeleteOptional type not supported".into()),
Expand Down Expand Up @@ -1335,6 +1358,7 @@ fn get_rust_type(
swagger20::SchemaKind::Ty(swagger20::Type::JSONSchemaPropsOrArray) |
swagger20::SchemaKind::Ty(swagger20::Type::JSONSchemaPropsOrBool) |
swagger20::SchemaKind::Ty(swagger20::Type::JSONSchemaPropsOrStringArray) => Err("JSON schema types not supported".into()),
swagger20::SchemaKind::Ty(swagger20::Type::Patch) => Err("Patch type not supported".into()),

swagger20::SchemaKind::Ty(swagger20::Type::WatchEvent(_)) => Err("WatchEvent type not supported".into()),
swagger20::SchemaKind::Ty(swagger20::Type::DeleteOptional(_)) => Err("DeleteOptional type not supported".into()),
Expand Down Expand Up @@ -1688,14 +1712,38 @@ pub fn write_operation(
.or_else(|| parameters.iter().find(|(_, _, parameter)| parameter.location == swagger20::ParameterLocation::Body));

write!(out, "{} let __body = ", indent)?;
if let Some((parameter_name, _, parameter)) = body_parameter {
if let Some((parameter_name, parameter_type, parameter)) = body_parameter {
if parameter.required {
writeln!(out, "serde_json::to_vec(&{}).map_err({}::RequestError::Json)?;", parameter_name, crate_root)?;
if parameter_type.starts_with('&') {
writeln!(out, "serde_json::to_vec({}).map_err({}::RequestError::Json)?;", parameter_name, crate_root)?;
}
else {
writeln!(out, "serde_json::to_vec(&{}).map_err({}::RequestError::Json)?;", parameter_name, crate_root)?;
}
}
else {
writeln!(out)?;
writeln!(out, "{}.unwrap_or(Ok(vec![]), |value| serde_json::to_vec(value).map_err({}::RequestError::Json))?;", parameter_name, crate_root)?;
}

let is_patch =
if let swagger20::SchemaKind::Ref(ref_path) = &parameter.schema.kind {
ref_path.path == "io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
else {
false
};
if is_patch {
let patch_type = get_rust_type(&parameter.schema.kind, replace_namespaces, crate_root, mod_root)?;
writeln!(out, "{} __request.header(http::header::CONTENT_TYPE, http::header::HeaderValue::from_static(match {} {{", indent, parameter_name)?;
writeln!(out, r#"{} {}::Json(_) => "application/json-patch+json","#, indent, patch_type)?;
writeln!(out, r#"{} {}::Merge(_) => "application/merge-patch+json","#, indent, patch_type)?;
writeln!(out, r#"{} {}::StrategicMerge(_) => "application/strategic-merge-patch+json","#, indent, patch_type)?;
writeln!(out, "{} }}));", indent)?;
}
else {
writeln!(out, r#"{} __request.header(http::header::CONTENT_TYPE, http::header::HeaderValue::from_static("application/json"));"#, indent)?;
}
}
else {
writeln!(out, "vec![];")?;
Expand Down Expand Up @@ -1769,17 +1817,28 @@ pub fn write_operation(

for &(_, variant_name, schema, is_delete_ok_status) in &operation_responses {
if is_delete_ok_status {
// DELETE operations that return metav1.Status for HTTP 200 can also return the object itself instead.
// Delete and delete-collection operations that return metav1.Status for HTTP 200 can also return the object itself instead.
//
// In case of delete-collection operations, this is the list object corresponding to the associated type.
//
// Ref https://github.com/kubernetes/kubernetes/issues/59501

writeln!(out, " {}Status({}),", variant_name, get_rust_type(&schema.kind, replace_namespaces, crate_root, mod_root)?)?;
writeln!(out, " {}Value({}),", variant_name, get_fully_qualified_type_name(
type_name_and_ref_path.as_ref()
.map(|(_, type_ref_path)| type_ref_path)
.ok_or_else(|| "DELETE-Ok-Status that isn't associated with a type")?,
&replace_namespaces,
crate_root,
mod_root)?)?;

let associated_type =
get_fully_qualified_type_name(
type_name_and_ref_path.as_ref()
.map(|(_, type_ref_path)| type_ref_path)
.ok_or_else(|| "DELETE-Ok-Status that isn't associated with a type")?,
&replace_namespaces,
crate_root,
mod_root)?;
if operation.kubernetes_action == Some(swagger20::KubernetesAction::DeleteCollection) {
writeln!(out, " {}Value({}List),", variant_name, associated_type)?;
}
else {
writeln!(out, " {}Value({}),", variant_name, associated_type)?;
}
}
else {
match &schema.kind {
Expand Down
1 change: 1 addition & 0 deletions k8s-openapi-codegen-common/src/swagger20/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ pub enum Type {
JSONSchemaPropsOrArray,
JSONSchemaPropsOrBool,
JSONSchemaPropsOrStringArray,
Patch,
WatchEvent(RefPath),

// Special types for common parameters of delete, list and watch operations respectively
Expand Down
11 changes: 11 additions & 0 deletions k8s-openapi-codegen/src/fixups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,17 @@ pub(crate) mod optional_properties {
}
}

// Annotate the `patch` type as `swagger20::Type::Patch` for special codegen.
pub(crate) fn patch(spec: &mut crate::swagger20::Spec) -> Result<(), crate::Error> {
let definition_path = crate::swagger20::DefinitionPath("io.k8s.apimachinery.pkg.apis.meta.v1.Patch".to_owned());
if let Some(definition) = spec.definitions.get_mut(&definition_path) {
definition.kind = crate::swagger20::SchemaKind::Ty(crate::swagger20::Type::Patch);
return Ok(());
}

Err("never applied Patch override".into())
}

// The spec says that `RawExtension` is an object with a property `raw` that's a byte-formatted string.
// While the golang type is indeed a struct with a `Raw []byte` field, the type is serialized by just emitting the value of that field.
// The value of that field is itself a JSON-serialized value. For example, a `WatchEvent` of `Pod`s has the `Pod` object serialized as
Expand Down
1 change: 1 addition & 0 deletions k8s-openapi-codegen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ fn run(supported_version: supported_version::SupportedVersion, out_dir_base: &st
&spec.definitions,
&mut spec.operations,
definition_path,
swagger20::RefPathRelativeTo::Crate,
replace_namespaces,
"crate",
Some(mod_root),
Expand Down
8 changes: 8 additions & 0 deletions k8s-openapi-codegen/src/supported_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl SupportedVersion {
crate::fixups::optional_properties::apigroup,
crate::fixups::optional_properties::crdstatus,
crate::fixups::optional_properties::poddisruptionbudgetstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_compat_refs,
crate::fixups::remove_delete_collection_operations_query_parameters,
Expand All @@ -87,6 +88,7 @@ impl SupportedVersion {
crate::fixups::optional_properties::apigroup,
crate::fixups::optional_properties::crdstatus,
crate::fixups::optional_properties::poddisruptionbudgetstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_compat_refs,
crate::fixups::remove_delete_collection_operations_query_parameters,
Expand All @@ -111,6 +113,7 @@ impl SupportedVersion {
crate::fixups::optional_properties::apigroup,
crate::fixups::optional_properties::crdstatus,
crate::fixups::optional_properties::poddisruptionbudgetstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_compat_refs,
crate::fixups::remove_delete_collection_operations_query_parameters,
Expand All @@ -127,6 +130,7 @@ impl SupportedVersion {
crate::fixups::json_ty::json_schema_props_or_string_array,
crate::fixups::optional_properties::crdstatus,
crate::fixups::optional_properties::poddisruptionbudgetstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_compat_refs,
crate::fixups::remove_delete_collection_operations_query_parameters,
Expand All @@ -142,6 +146,7 @@ impl SupportedVersion {
crate::fixups::json_ty::json_schema_props_or_bool,
crate::fixups::json_ty::json_schema_props_or_string_array,
crate::fixups::optional_properties::crdstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_compat_refs,
crate::fixups::remove_delete_collection_operations_query_parameters,
Expand All @@ -157,6 +162,7 @@ impl SupportedVersion {
crate::fixups::json_ty::json_schema_props_or_bool,
crate::fixups::json_ty::json_schema_props_or_string_array,
crate::fixups::optional_properties::crdstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_compat_refs,
crate::fixups::remove_delete_collection_operations_query_parameters,
Expand All @@ -172,6 +178,7 @@ impl SupportedVersion {
crate::fixups::json_ty::json_schema_props_or_bool,
crate::fixups::json_ty::json_schema_props_or_string_array,
crate::fixups::optional_properties::crdstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_delete_collection_operations_query_parameters,
crate::fixups::remove_delete_operations_query_parameters,
Expand All @@ -186,6 +193,7 @@ impl SupportedVersion {
crate::fixups::json_ty::json_schema_props_or_bool,
crate::fixups::json_ty::json_schema_props_or_string_array,
crate::fixups::optional_properties::crdstatus,
crate::fixups::patch,
crate::fixups::raw_extension_ty,
crate::fixups::remove_delete_collection_operations_query_parameters,
crate::fixups::remove_delete_operations_query_parameters,
Expand Down
2 changes: 2 additions & 0 deletions k8s-openapi-derive/src/custom_resource_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ impl super::CustomDerive for CustomResourceDefinition {
&spec.definitions,
&mut spec.operations,
&swagger20::DefinitionPath(cr_name.clone()),
swagger20::RefPathRelativeTo::Scope,
&[
(&["io".into(), "k8s".into()], &[]),
],
Expand All @@ -851,6 +852,7 @@ impl super::CustomDerive for CustomResourceDefinition {
&spec.definitions,
&mut spec.operations,
&swagger20::DefinitionPath(cr_list_name.clone()),
swagger20::RefPathRelativeTo::Scope,
&[
(&["io".into(), "k8s".into()], &[]),
],
Expand Down
32 changes: 16 additions & 16 deletions k8s-openapi-tests/src/custom_resource_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ fn test() {
}

// Wait for CRD to be registered
let custom_resource_definition = loop {
loop {
let (request, response_body) =
apiextensions::CustomResourceDefinition::read_custom_resource_definition(
&format!("{}.{}", plural, <FooBar as k8s_openapi::Resource>::group().to_owned()), Default::default())
Expand All @@ -138,11 +138,11 @@ fn test() {
};

if custom_resource_definition.status.as_ref().map_or(false, |status| status.accepted_names.kind == "FooBar") {
break custom_resource_definition;
break;
}

client.sleep(std::time::Duration::from_secs(1));
};
}


// Create CR
Expand Down Expand Up @@ -262,6 +262,7 @@ fn test() {
let request =
http::Request::post(format!("/apis/{}/{}/namespaces/default/{}",
<FooBar as k8s_openapi::Resource>::group(), <FooBar as k8s_openapi::Resource>::version(), plural))
.header(http::header::CONTENT_TYPE, "application/json")
.body(serde_json::to_vec(&fb2).expect("couldn't create custom resource definition"))
.expect("couldn't create custom resource");
{
Expand All @@ -286,6 +287,7 @@ fn test() {
let request =
http::Request::post(format!("/apis/{}/{}/namespaces/default/{}",
<FooBar as k8s_openapi::Resource>::group(), <FooBar as k8s_openapi::Resource>::version(), plural))
.header(http::header::CONTENT_TYPE, "application/json")
.body(serde_json::to_vec(&fb3).expect("couldn't create custom resource definition"))
.expect("couldn't create custom resource");
{
Expand All @@ -299,18 +301,16 @@ fn test() {


// Delete CRD
let custom_resource_definition_self_link = {
let metadata = custom_resource_definition.metadata.expect("couldn't get custom resource definition metadata");
metadata.self_link.expect("couldn't get custom resource definition self link")
};
let request = http::Request::delete(custom_resource_definition_self_link).body(vec![]).expect("couldn't delete custom resource definition");
{
let response = client.execute(request).expect("couldn't delete custom resource definition");
crate::get_single_value(response, k8s_openapi::ResponseBody::new, |response, status_code| match response {
apiextensions::DeleteCollectionCustomResourceDefinitionResponse::OkStatus(_) |
apiextensions::DeleteCollectionCustomResourceDefinitionResponse::OkValue(_) => Ok(crate::ValueResult::GotValue(())),
other => Err(format!("{:?} {}", other, status_code).into()),
}).expect("couldn't delete custom resource definition");
}
let (request, response_body) =
apiextensions::CustomResourceDefinition::delete_custom_resource_definition(
&format!("{}.{}", plural, <FooBar as k8s_openapi::Resource>::group()),
Default::default())
.expect("couldn't delete custom resource definition");
let response = client.execute(request).expect("couldn't delete custom resource definition");
crate::get_single_value(response, response_body, |response, status_code| match response {
apiextensions::DeleteCustomResourceDefinitionResponse::OkStatus(_) |
apiextensions::DeleteCustomResourceDefinitionResponse::OkValue(_) => Ok(crate::ValueResult::GotValue(())),
other => Err(format!("{:?} {}", other, status_code).into()),
}).expect("couldn't delete custom resource definition");
});
}
34 changes: 13 additions & 21 deletions k8s-openapi-tests/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,29 +153,21 @@ fn create() {

// Delete all pods of the job using label selector
let (request, response_body) =
api::Pod::list_namespaced_pod("default", k8s_openapi::ListOptional {
label_selector: Some("job-name=k8s-openapi-tests-create-job"),
..Default::default()
})
.expect("couldn't list pods");
let pod_list = {
let response = client.execute(request).expect("couldn't list pods");
api::Pod::delete_collection_namespaced_pod(
"default",
Default::default(),
k8s_openapi::ListOptional {
label_selector: Some("job-name=k8s-openapi-tests-create-job"),
..Default::default()
},
)
.expect("couldn't delete pods collection");
{
let response = client.execute(request).expect("couldn't delete pods collection");
crate::get_single_value(response, response_body, |response, status_code| match response {
api::ListNamespacedPodResponse::Ok(pod_list) => Ok(crate::ValueResult::GotValue(pod_list)),
other => Err(format!("{:?} {}", other, status_code).into()),
}).expect("couldn't list pods")
};

for pod in pod_list.items {
let self_link =
pod.metadata.expect("couldn't get job pod metadata")
.self_link.expect("couldn't get job pod self link");
let request = http::Request::delete(self_link).body(vec![]).expect("couldn't delete job pod");
let response = client.execute(request).expect("couldn't delete job pod");
crate::get_single_value(response, k8s_openapi::ResponseBody::new, |response, status_code| match response {
api::DeleteNamespacedPodResponse::OkStatus(_) | api::DeleteNamespacedPodResponse::OkValue(_) => Ok(crate::ValueResult::GotValue(())),
api::DeleteCollectionNamespacedPodResponse::OkStatus(_) | api::DeleteCollectionNamespacedPodResponse::OkValue(_) => Ok(crate::ValueResult::GotValue(())),
other => Err(format!("{:?} {}", other, status_code).into()),
}).expect("couldn't delete job pod");
}).expect("couldn't delete pods collection");
}
});
}
Loading

0 comments on commit 36334e6

Please sign in to comment.