package handler
import (
"bytes"
"context"
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/discovery"
"github.com/goodrain/rainbond/pkg/component/k8s"
httputil "github.com/goodrain/rainbond/util/http"
)
type ResourceTypeInfo struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Resource string `json:"resource"`
Verbs []string `json:"verbs"`
}
type ClusterResourceHandler struct{}
func (h *ClusterResourceHandler) ListResourceTypes() ([]ResourceTypeInfo, error) {
dc := k8s.Default().Clientset.Discovery()
_, resList, err := dc.ServerGroupsAndResources()
if err != nil && !discovery.IsGroupDiscoveryFailedError(err) {
return nil, err
}
var types []ResourceTypeInfo
for _, rl := range resList {
gv, parseErr := schema.ParseGroupVersion(rl.GroupVersion)
if parseErr != nil {
continue
}
for _, r := range rl.APIResources {
if !r.Namespaced && !containsSlash(r.Name) {
types = append(types, ResourceTypeInfo{
Group: gv.Group,
Version: gv.Version,
Kind: r.Kind,
Resource: r.Name,
Verbs: r.Verbs,
})
}
}
}
return types, nil
}
func (h *ClusterResourceHandler) ListResources(group, version, resource string) ([]unstructured.Unstructured, error) {
if err := validateGVRParams(group, version, resource); err != nil {
return nil, err
}
gvr := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
list, err := k8s.Default().DynamicClient.Resource(gvr).List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}
return list.Items, nil
}
func (h *ClusterResourceHandler) GetResource(group, version, resource, name string) (*unstructured.Unstructured, error) {
if err := validateGVRParams(group, version, resource); err != nil {
return nil, err
}
gvr := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
return k8s.Default().DynamicClient.Resource(gvr).Get(context.Background(), name, metav1.GetOptions{})
}
func (h *ClusterResourceHandler) CreateResource(group, version, resource string, yamlBody []byte) (*unstructured.Unstructured, error) {
if err := validateGVRParams(group, version, resource); err != nil {
return nil, err
}
obj := &unstructured.Unstructured{}
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlBody), 4096)
if err := decoder.Decode(obj); err != nil {
return nil, httputil.NewErrBadRequest(fmt.Errorf("invalid YAML: %v", err))
}
gvr := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
result, err := k8s.Default().DynamicClient.Resource(gvr).Create(context.Background(), obj, metav1.CreateOptions{})
if err != nil && (errors.IsInvalid(err) || errors.IsBadRequest(err)) {
return nil, httputil.NewErrBadRequest(err)
}
return result, err
}
func (h *ClusterResourceHandler) UpdateResource(group, version, resource, name string, yamlBody []byte) (*unstructured.Unstructured, error) {
if err := validateGVRParams(group, version, resource); err != nil {
return nil, err
}
gvr := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
obj := &unstructured.Unstructured{}
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlBody), 4096)
if err := decoder.Decode(obj); err != nil {
return nil, httputil.NewErrBadRequest(fmt.Errorf("invalid YAML: %v", err))
}
if obj.GetResourceVersion() == "" {
current, err := k8s.Default().DynamicClient.Resource(gvr).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
obj.SetResourceVersion(current.GetResourceVersion())
}
result, err := k8s.Default().DynamicClient.Resource(gvr).Update(context.Background(), obj, metav1.UpdateOptions{})
if err != nil && (errors.IsInvalid(err) || errors.IsBadRequest(err)) {
return nil, httputil.NewErrBadRequest(err)
}
return result, err
}
func (h *ClusterResourceHandler) DeleteResource(group, version, resource, name string) error {
if err := validateGVRParams(group, version, resource); err != nil {
return err
}
gvr := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
err := k8s.Default().DynamicClient.Resource(gvr).Delete(context.Background(), name, metav1.DeleteOptions{})
if errors.IsNotFound(err) {
return nil
}
return err
}
func validateGVRParams(group, version, resource string) error {
if version == "" {
return fmt.Errorf("version is required")
}
if resource == "" {
return fmt.Errorf("resource is required")
}
return nil
}
func containsSlash(s string) bool {
for _, c := range s {
if c == '/' {
return true
}
}
return false
}
var clusterResourceHandler *ClusterResourceHandler
func GetClusterResourceHandler() *ClusterResourceHandler {
if clusterResourceHandler == nil {
clusterResourceHandler = &ClusterResourceHandler{}
}
return clusterResourceHandler
}