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

Load any Submit resources without mentioning kind types and api group/versions #3336 #3744

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
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
111 changes: 102 additions & 9 deletions util/src/main/java/io/kubernetes/client/util/Yaml.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
package io.kubernetes.client.util;

import com.google.gson.annotations.SerializedName;
import io.kubernetes.client.common.KubernetesObject;
import io.kubernetes.client.common.KubernetesType;
import io.kubernetes.client.custom.IntOrString;
import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import io.kubernetes.client.openapi.models.V1JSONSchemaProps;
import java.io.File;
import java.io.FileReader;
Expand All @@ -26,15 +30,10 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.*;
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove wildcard imports.


import io.kubernetes.client.util.generic.GenericKubernetesApi;
import io.kubernetes.client.util.generic.KubernetesApiResponse;
import okio.ByteString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -59,6 +58,100 @@ public class Yaml {

static final Logger logger = LoggerFactory.getLogger(Yaml.class);

/**
* Load an API object from a YAML string representation without already knowing the object type and loads and submit any type of resource
* V1Pod
*
* @param content The YAML content
* @return An instantiation of the object.
* @throws IOException If an error occurs while reading the YAML.
*/
public static Object loadAndSubmitResource(String content) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's more flexible to load from a Reader rather than a String you can always create a StringReader

//loading yaml content as unstructured object
Object unstructuredObject = Yaml.load(new StringReader(content));

if (!(unstructuredObject instanceof Map)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

fwiw, this will only work for single-instance YAML files, if people put multiple objects in a single file, it will fail. That's fine for a starting point, but you may want to document it somewhere.

throw new IllegalArgumentException("Invalid YAML");
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 ParseException might be more appropriate? (here and below)

}
//typecasting the unstructured object in map
Map<String, Object> yamlMap = (Map<String, Object>) unstructuredObject;

//getting api version and kind
String apiVersion = (String) yamlMap.get("apiVersion");
String kind = (String) yamlMap.get("kind");

if (apiVersion == null || kind == null) {
throw new IllegalArgumentException("YAML does not contain apiVersion or kind");
}

//creating variable to extract api group
String group;
String version;

// If apiVersion contains '/', then it means there is a specified group for it and we need to get that else it is a default group
if (apiVersion.contains("/")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This code is already present here:

public static GroupVersion parse(String apiVersion) {

Let's re-use that instead of reimplementing.

String[] parts = apiVersion.split("/");
group = parts[0];
version = parts[1];
} else {
group = "";
version = apiVersion;
}

//creating resource plurals from kind
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using the Discovery class here instead

String resourcePlural = kind.toLowerCase(Locale.ROOT) + "s"; // Simplistic pluralization logic

//getting the strongly typed object from model mapper
Class<?> modelClass = ModelMapper.getApiTypeClass(group, version, kind);
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 you can say Class<? implements KubernetesObject> here?


if (modelClass == null) {
throw new IllegalArgumentException("Could not determine model class for group: "
+ group + ", version: " + version + ", kind: " + kind);
}

//reloading the yaml in strongly typed object
KubernetesObject typedObject = (KubernetesObject) Yaml.loadAs(new StringReader(content), modelClass);

//we need to submit the resource to generickubernetesapi
return submitResourceToApi(typedObject, (Class<KubernetesObject>) modelClass, group, version, resourcePlural);
}

//method to submit any resource to kubernetes api
public static <ApiType extends KubernetesObject> Object submitResourceToApi(
ApiType resource,
Class<ApiType> apiTypeClass,
String group,
String version,
String resourcePlural) throws Exception {

//creating an api client and configuring it
ApiClient client = Config.defaultClient();
Configuration.setDefaultApiClient(client);

//Using the apiTypeClass and apiListTypeClass for list of resources
Class<?> apiListTypeClass = ModelMapper.getApiTypeClass(group, version, resourcePlural);

//configuring the GenericKubernetesApi handler
GenericKubernetesApi<ApiType, ?> genericApi = new GenericKubernetesApi<>(
apiTypeClass,
apiListTypeClass,
group,
version,
resourcePlural,
new CustomObjectsApi(client)
);

//Creating the resource in Kubernetes
KubernetesApiResponse<ApiType> apiResponse = genericApi.create(resource);

if (!apiResponse.isSuccess()) {
throw new RuntimeException("Failed to create resource: " + apiResponse.getStatus());
}

//return the created resource
return apiResponse.getObject();
}

/**
* Load an API object from a YAML string representation. Returns a concrete typed object (e.g.
* V1Pod)
Expand Down