Arnavion / k8s-openapi

Rust definitions of the resource types in the Kubernetes client API
Apache License 2.0
386 stars 41 forks source link

Namespaces are not Serializing #155

Closed RileySeaburg closed 8 months ago

RileySeaburg commented 8 months ago

Great project here,

Just a note when trying to create any services inside a cluster using the API object leaves a error that comes up on basically every kubernetes object type.


#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)]
pub struct KubernetesServiceResponse{
    namespace: String,
    serviceName: String,
    configMapName: String,
    ingressName: String,
    deploymentName: String,
}

impl KubernetesServiceResponse {
    pub fn new() -> KubernetesServiceResponse {
        KubernetesServiceResponse {
            namespace: "".to_string(),
            serviceName: "".to_string(),
            configMapName: "".to_string(),
            ingressName: "".to_string(),
            deploymentName: "".to_string(),
        }
    }
}

pub async fn deploy_kubernetes_service(
    domain: String,
    email: String,
    public_key: String,
    client_id: String,
    client_secret: String,
) -> Result<KubernetesServiceResponse, color_eyre::eyre::Error> {
    let mut kubernetes_service_response = KubernetesServiceResponse::new();
    let rand_number = rand::random::<u32>();
    let namespace_name_fallback = format!("quantum-forge-customer-{}", rand_number);
    let mut name_space_name = &domain.split(".").next().unwrap_or(&namespace_name_fallback);

// defining variables
    // defining yaml values to avoid string interpolation issues
    let ingress_name = &domain.split('.').next().unwrap_or("quantum-forge-customer");
    let ingress_host = &domain;
    let ingress_service_name = &domain.split('.').next().unwrap_or("quantum-forge-customer");
    let ingress_tls_host = &domain;
    let ingress_tls_secret_name = &domain.split('.').next().unwrap_or("quantum-forge-customer");

    let service_yaml = serde_yaml::from_str(&format!(
        r#"apiVersion: v1
kind: Service
metadata:
  name: {}-service
  namespace: {}
spec:
  selector:
    app: {}-app
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80
    "#,
        &name_space_name,
        &name_space_name,
        &name_space_name
    )).unwrap();

// create the api interface
  let service_api = Api::<Service>::namespaced(client.clone(), domain.clone().split("").next().unwrap_or("quanutum-forge-customer"));

      // apply the service
    service_api
        .create(&PostParams::default(), &service_yaml)
        .await
        .unwrap();

    // check that the service was created
    loop {
        match service_api.get("quantum-forge-customer-platform").await {
            Ok(service) => {
                kubernetes_service_response.serviceName = service.clone().metadata.name.unwrap();
                println!(
                    "Service {} is ready.",
                    service.metadata.name.unwrap_or_default()
                );
                break;
            }
            Err(e) => {
                println!("Error checking service: {:?}", e);

                tokio::time::sleep(Duration::from_secs(5)).await;
            }
        }
    }
    ...more code here for services

        Ok(kubernetes_service_response)
}
thread 'actix-rt|system:0|arbiter:0' panicked at 'called `Result::unwrap()` on an `Err` value: Api(ErrorResponse { status: "Failure", message: "Service \"rileyseaburg-service\" is invalid: metadata.namespace: Required value", reason: "Invalid", code: 422 })', src\controllers\api\onboarding.rs:436:10

As you can see the Namespace for the service is clearly defined using an object from earlier. When we runt the print on this object the namespace is represented as Some("namespace_name");

It appears there is a serialization error, but it's not happening when the service is defined, rather its occurring when the API is called, and the service is being created.

clux commented 8 months ago

this is more kube related than k8s-openapi since you are bypassing the structs entirely. i don't use create much myself since it's an older pattern, but on a suspicious note, are you passing string serialised yaml to the api (which takes json)? see a normal create example.

you can also increase the log level of kube to see what goes on the wire.

RileySeaburg commented 8 months ago

Hey @clux I appreciate your swift response.

I'm not sure what you referring to in regards to "bypassing the structs".

I can confirm that the Yaml version does work for creating namespaces. It also works for creating deployments.

It just seems to get stuck on other items such as ingresses.

I'll go ahead and serialize my yaml to json and see what I come up with.

Thanks again and great project.

Arnavion commented 8 months ago

@clux I'm surprised this works as well, but also the doc of Api::create does specifically allow it:

This function requires a type that Serializes to K, which can be:

  1. Raw string YAML
RileySeaburg commented 8 months ago

Lol @Arnavion

error prone (run-time errors on typos due to failed serialize attempts)

Can confirm this is true.