@@ -566,4 +566,157 @@ private static Object modelMapper(Map<String, Object> data) throws IOException {
566566 public static void addModelMap (String apiGroupVersion , String kind , Class <?> clazz ) {
567567 ModelMapper .addModelMap (apiGroupVersion , kind , clazz );
568568 }
569+
570+ /**
571+ * Create a Kubernetes resource from a YAML string. This method automatically determines the
572+ * resource type from the YAML content (apiVersion and kind) and uses the appropriate API to
573+ * create the resource.
574+ *
575+ * <p>This is equivalent to `kubectl create -f <yaml-content>`.
576+ *
577+ * <p>Example usage:
578+ * <pre>{@code
579+ * ApiClient client = Config.defaultClient();
580+ * String yaml = "apiVersion: v1\n" +
581+ * "kind: ConfigMap\n" +
582+ * "metadata:\n" +
583+ * " name: my-config\n" +
584+ * " namespace: default\n";
585+ * Object created = Yaml.createResource(client, yaml);
586+ * }</pre>
587+ *
588+ * @param client The API client to use for creating the resource
589+ * @param content The YAML content as a string
590+ * @return The created resource object
591+ * @throws IOException If an error occurs while reading or parsing the YAML
592+ * @throws io.kubernetes.client.openapi.ApiException If an error occurs while creating the resource in the cluster
593+ */
594+ public static Object createResource (io .kubernetes .client .openapi .ApiClient client , String content )
595+ throws IOException , io .kubernetes .client .openapi .ApiException {
596+ return createResource (client , new StringReader (content ));
597+ }
598+
599+ /**
600+ * Create a Kubernetes resource from a YAML file. This method automatically determines the
601+ * resource type from the YAML content (apiVersion and kind) and uses the appropriate API to
602+ * create the resource.
603+ *
604+ * <p>This is equivalent to `kubectl create -f <yaml-file>`.
605+ *
606+ * @param client The API client to use for creating the resource
607+ * @param f The YAML file to load
608+ * @return The created resource object
609+ * @throws IOException If an error occurs while reading or parsing the YAML
610+ * @throws io.kubernetes.client.openapi.ApiException If an error occurs while creating the resource in the cluster
611+ */
612+ public static Object createResource (io .kubernetes .client .openapi .ApiClient client , File f )
613+ throws IOException , io .kubernetes .client .openapi .ApiException {
614+ return createResource (client , new FileReader (f ));
615+ }
616+
617+ /**
618+ * Create a Kubernetes resource from a YAML stream. This method automatically determines the
619+ * resource type from the YAML content (apiVersion and kind) and uses the appropriate API to
620+ * create the resource.
621+ *
622+ * <p>This is equivalent to `kubectl create -f <yaml-stream>`.
623+ *
624+ * @param client The API client to use for creating the resource
625+ * @param reader The stream to load
626+ * @return The created resource object
627+ * @throws IOException If an error occurs while reading or parsing the YAML
628+ * @throws io.kubernetes.client.openapi.ApiException If an error occurs while creating the resource in the cluster
629+ */
630+ public static Object createResource (io .kubernetes .client .openapi .ApiClient client , Reader reader )
631+ throws IOException , io .kubernetes .client .openapi .ApiException {
632+ // Load the YAML as a map to extract apiVersion and kind
633+ Map <String , Object > data = getSnakeYaml (null ).load (reader );
634+
635+ String kind = (String ) data .get ("kind" );
636+ if (kind == null ) {
637+ throw new IOException ("Missing kind in YAML!" );
638+ }
639+ String apiVersion = (String ) data .get ("apiVersion" );
640+ if (apiVersion == null ) {
641+ throw new IOException ("Missing apiVersion in YAML!" );
642+ }
643+
644+ // Use ModelMapper to get the appropriate class for this resource type
645+ Class <?> clazz = ModelMapper .getApiTypeClass (apiVersion , kind );
646+ if (clazz == null ) {
647+ throw new IOException (
648+ "Unknown apiVersion/kind: " + apiVersion + "/" + kind + ". Is it registered?" );
649+ }
650+
651+ // Load the YAML into the strongly typed object
652+ Object resource = loadAs (new StringReader (getSnakeYaml (clazz ).dump (data )), clazz );
653+
654+ // Ensure the resource is a KubernetesObject
655+ if (!(resource instanceof io .kubernetes .client .common .KubernetesObject )) {
656+ throw new IOException (
657+ "Resource is not a KubernetesObject: " + resource .getClass ().getName ());
658+ }
659+
660+ io .kubernetes .client .common .KubernetesObject k8sObject =
661+ (io .kubernetes .client .common .KubernetesObject ) resource ;
662+
663+ // Parse apiVersion to extract group and version
664+ io .kubernetes .client .apimachinery .GroupVersionKind gvk =
665+ ModelMapper .groupVersionKindFromApiVersionAndKind (apiVersion , kind );
666+
667+ // Get the resource metadata to determine the plural name
668+ io .kubernetes .client .apimachinery .GroupVersionResource gvr =
669+ ModelMapper .getGroupVersionResourceByClass (clazz );
670+
671+ if (gvr == null ) {
672+ // If no GVR mapping exists, we need to perform discovery
673+ io .kubernetes .client .Discovery discovery = new io .kubernetes .client .Discovery (client );
674+ ModelMapper .refresh (discovery );
675+ gvr = ModelMapper .getGroupVersionResourceByClass (clazz );
676+
677+ if (gvr == null ) {
678+ throw new IOException (
679+ "Unable to determine resource plural name for " + apiVersion + "/" + kind );
680+ }
681+ }
682+
683+ // Create a GenericKubernetesApi for this resource type
684+ io .kubernetes .client .util .generic .GenericKubernetesApi <
685+ io .kubernetes .client .common .KubernetesObject ,
686+ io .kubernetes .client .common .KubernetesListObject >
687+ api =
688+ new io .kubernetes .client .util .generic .GenericKubernetesApi <>(
689+ (Class <io .kubernetes .client .common .KubernetesObject >) clazz ,
690+ io .kubernetes .client .common .KubernetesListObject .class ,
691+ gvk .getGroup (),
692+ gvk .getVersion (),
693+ gvr .getResource (),
694+ client );
695+
696+ // Create the resource
697+ io .kubernetes .client .util .generic .KubernetesApiResponse <
698+ io .kubernetes .client .common .KubernetesObject >
699+ response ;
700+
701+ Boolean isNamespaced = ModelMapper .isNamespaced (clazz );
702+ if (isNamespaced != null && isNamespaced ) {
703+ // For namespaced resources
704+ String namespace = k8sObject .getMetadata ().getNamespace ();
705+ if (namespace == null || namespace .isEmpty ()) {
706+ namespace = "default" ;
707+ }
708+ response = api .create (namespace , k8sObject , new io .kubernetes .client .util .generic .options .CreateOptions ());
709+ } else {
710+ // For cluster-scoped resources
711+ response = api .create (k8sObject , new io .kubernetes .client .util .generic .options .CreateOptions ());
712+ }
713+
714+ if (!response .isSuccess ()) {
715+ throw new io .kubernetes .client .openapi .ApiException (
716+ response .getHttpStatusCode (),
717+ "Failed to create resource: " + response .getStatus ());
718+ }
719+
720+ return response .getObject ();
721+ }
569722}
0 commit comments