package client import ( "context" "fmt" apierrors "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/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) var environmentGVR = schema.GroupVersionResource{ Group: "fission.io", Version: "v1", Resource: "environments", } var packageGVR = schema.GroupVersionResource{ Group: "fission.io", Version: "v1", Resource: "packages", } var functionGVR = schema.GroupVersionResource{ Group: "fission.io", Version: "v1", Resource: "functions", } var httpTriggerGVR = schema.GroupVersionResource{ Group: "fission.io", Version: "v1", Resource: "httptriggers", } // Client хранит клиентов Kubernetes API для работы с CRD Fission. type Client struct { DynClient dynamic.Interface K8sClient kubernetes.Interface Namespace string } // New создает Kubernetes clients из kubeconfig/context. func New(kubeconfigPath, kubeContext, namespace string) (*Client, error) { cfg, err := buildConfig(kubeconfigPath, kubeContext) if err != nil { return nil, err } dynClient, err := dynamic.NewForConfig(cfg) if err != nil { return nil, fmt.Errorf("create dynamic client: %w", err) } k8sClient, err := kubernetes.NewForConfig(cfg) if err != nil { return nil, fmt.Errorf("create kubernetes client: %w", err) } return &Client{ DynClient: dynClient, K8sClient: k8sClient, Namespace: namespace, }, nil } func buildConfig(kubeconfigPath, kubeContext string) (*rest.Config, error) { loadingRules := &clientcmd.ClientConfigLoadingRules{} if kubeconfigPath != "" { loadingRules.ExplicitPath = kubeconfigPath } overrides := &clientcmd.ConfigOverrides{} if kubeContext != "" { overrides.CurrentContext = kubeContext } clientCfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) cfg, err := clientCfg.ClientConfig() if err != nil { return nil, fmt.Errorf("build kube config: %w", err) } return cfg, nil } // CreateEnvironment создает объект Environment в заданном namespace. func (c *Client) CreateEnvironment(ctx context.Context, environment *unstructured.Unstructured) (*unstructured.Unstructured, error) { createdEnvironment, err := c.DynClient.Resource(environmentGVR).Namespace(environment.GetNamespace()).Create(ctx, environment, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("create environment %q: %w", environment.GetName(), err) } return createdEnvironment, nil } // GetEnvironment получает объект Environment по имени. func (c *Client) GetEnvironment(ctx context.Context, namespace, name string) (*unstructured.Unstructured, error) { environment, err := c.DynClient.Resource(environmentGVR).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("get environment %q: %w", name, err) } return environment, nil } // UpdateEnvironment обновляет существующий объект Environment. func (c *Client) UpdateEnvironment(ctx context.Context, environment *unstructured.Unstructured) (*unstructured.Unstructured, error) { updatedEnvironment, err := c.DynClient.Resource(environmentGVR).Namespace(environment.GetNamespace()).Update(ctx, environment, metav1.UpdateOptions{}) if err != nil { return nil, fmt.Errorf("update environment %q: %w", environment.GetName(), err) } return updatedEnvironment, nil } // DeleteEnvironment удаляет объект Environment по имени. func (c *Client) DeleteEnvironment(ctx context.Context, namespace, name string) error { err := c.DynClient.Resource(environmentGVR).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{}) if err != nil { return fmt.Errorf("delete environment %q: %w", name, err) } return nil } // IsNotFound возвращает true для ошибок Kubernetes типа NotFound. func IsNotFound(err error) bool { return apierrors.IsNotFound(err) } // CreatePackage создает объект Package в заданном namespace. func (c *Client) CreatePackage(ctx context.Context, pkg *unstructured.Unstructured) (*unstructured.Unstructured, error) { createdPackage, err := c.DynClient.Resource(packageGVR).Namespace(pkg.GetNamespace()).Create(ctx, pkg, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("create package %q: %w", pkg.GetName(), err) } return createdPackage, nil } // GetPackage получает объект Package по имени. func (c *Client) GetPackage(ctx context.Context, namespace, name string) (*unstructured.Unstructured, error) { pkg, err := c.DynClient.Resource(packageGVR).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("get package %q: %w", name, err) } return pkg, nil } // UpdatePackage обновляет существующий объект Package. func (c *Client) UpdatePackage(ctx context.Context, pkg *unstructured.Unstructured) (*unstructured.Unstructured, error) { updatedPackage, err := c.DynClient.Resource(packageGVR).Namespace(pkg.GetNamespace()).Update(ctx, pkg, metav1.UpdateOptions{}) if err != nil { return nil, fmt.Errorf("update package %q: %w", pkg.GetName(), err) } return updatedPackage, nil } // DeletePackage удаляет объект Package по имени. func (c *Client) DeletePackage(ctx context.Context, namespace, name string) error { err := c.DynClient.Resource(packageGVR).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{}) if err != nil { return fmt.Errorf("delete package %q: %w", name, err) } return nil } // CreateFunction создает объект Function в заданном namespace. func (c *Client) CreateFunction(ctx context.Context, fn *unstructured.Unstructured) (*unstructured.Unstructured, error) { createdFunction, err := c.DynClient.Resource(functionGVR).Namespace(fn.GetNamespace()).Create(ctx, fn, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("create function %q: %w", fn.GetName(), err) } return createdFunction, nil } // GetFunction получает объект Function по имени. func (c *Client) GetFunction(ctx context.Context, namespace, name string) (*unstructured.Unstructured, error) { fn, err := c.DynClient.Resource(functionGVR).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("get function %q: %w", name, err) } return fn, nil } // UpdateFunction обновляет существующий объект Function. func (c *Client) UpdateFunction(ctx context.Context, fn *unstructured.Unstructured) (*unstructured.Unstructured, error) { updatedFunction, err := c.DynClient.Resource(functionGVR).Namespace(fn.GetNamespace()).Update(ctx, fn, metav1.UpdateOptions{}) if err != nil { return nil, fmt.Errorf("update function %q: %w", fn.GetName(), err) } return updatedFunction, nil } // DeleteFunction удаляет объект Function по имени. func (c *Client) DeleteFunction(ctx context.Context, namespace, name string) error { err := c.DynClient.Resource(functionGVR).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{}) if err != nil { return fmt.Errorf("delete function %q: %w", name, err) } return nil } // CreateHTTPTrigger создает объект HTTPTrigger в заданном namespace. func (c *Client) CreateHTTPTrigger(ctx context.Context, trigger *unstructured.Unstructured) (*unstructured.Unstructured, error) { createdTrigger, err := c.DynClient.Resource(httpTriggerGVR).Namespace(trigger.GetNamespace()).Create(ctx, trigger, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("create http trigger %q: %w", trigger.GetName(), err) } return createdTrigger, nil } // GetHTTPTrigger получает объект HTTPTrigger по имени. func (c *Client) GetHTTPTrigger(ctx context.Context, namespace, name string) (*unstructured.Unstructured, error) { trigger, err := c.DynClient.Resource(httpTriggerGVR).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("get http trigger %q: %w", name, err) } return trigger, nil } // UpdateHTTPTrigger обновляет существующий объект HTTPTrigger. func (c *Client) UpdateHTTPTrigger(ctx context.Context, trigger *unstructured.Unstructured) (*unstructured.Unstructured, error) { updatedTrigger, err := c.DynClient.Resource(httpTriggerGVR).Namespace(trigger.GetNamespace()).Update(ctx, trigger, metav1.UpdateOptions{}) if err != nil { return nil, fmt.Errorf("update http trigger %q: %w", trigger.GetName(), err) } return updatedTrigger, nil } // DeleteHTTPTrigger удаляет объект HTTPTrigger по имени. func (c *Client) DeleteHTTPTrigger(ctx context.Context, namespace, name string) error { err := c.DynClient.Resource(httpTriggerGVR).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{}) if err != nil { return fmt.Errorf("delete http trigger %q: %w", name, err) } return nil }