From d5ad32e2e478834b992125e6cc3cc83cdaf830b7 Mon Sep 17 00:00:00 2001 From: pb Date: Fri, 11 Apr 2025 12:00:21 +0200 Subject: [PATCH] [NEED REFACTORING] added DynamicClient constructor to make API calls on CDRs --- infrastructure/kubernetes.go | 189 +++++++++++++++++++++++++++-------- 1 file changed, 150 insertions(+), 39 deletions(-) diff --git a/infrastructure/kubernetes.go b/infrastructure/kubernetes.go index 4944a26..b3c9463 100644 --- a/infrastructure/kubernetes.go +++ b/infrastructure/kubernetes.go @@ -1,13 +1,11 @@ package infrastructure import ( - "bytes" "context" "encoding/base64" "encoding/json" "errors" "fmt" - "html/template" "oc-datacenter/conf" "strings" @@ -16,13 +14,40 @@ import ( rbacv1 "k8s.io/api/rbac/v1" 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" apply "k8s.io/client-go/applyconfigurations/core/v1" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) +var gvrSources = schema.GroupVersionResource{Group: "multicluster.admiralty.io", Version: "v1alpha1", Resource: "sources"} +var gvrTargets = schema.GroupVersionResource{Group: "multicluster.admiralty.io", Version: "v1alpha1", Resource: "targets"} + type KubernetesService struct { - Set *kubernetes.Clientset + Set *kubernetes.Clientset +} + +func NewDynamicClient() (*dynamic.DynamicClient, error) { + config := &rest.Config{ + Host: conf.GetConfig().KubeHost + ":" + conf.GetConfig().KubePort, + TLSClientConfig: rest.TLSClientConfig{ + CAData: []byte(conf.GetConfig().KubeCA), + CertData: []byte(conf.GetConfig().KubeCert), + KeyData: []byte(conf.GetConfig().KubeData), + }, + } + + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, errors.New("Error creating Dynamic client: " + err.Error()) + } + if dynamicClient == nil { + return nil, errors.New("Error creating Dynamic client: dynamicClient is nil") + } + + return dynamicClient, nil } func NewKubernetesService() (Infrastructure, error) { @@ -34,6 +59,7 @@ func NewKubernetesService() (Infrastructure, error) { KeyData: []byte(conf.GetConfig().KubeData), }, } + // Create clientset clientset, err := kubernetes.NewForConfig(config) fmt.Println("NewForConfig", clientset, err) @@ -44,6 +70,7 @@ func NewKubernetesService() (Infrastructure, error) { return nil, errors.New("Error creating Kubernetes client: clientset is nil") } + return &KubernetesService{ Set: clientset, }, nil @@ -267,32 +294,73 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context,execut return nil, nil // Maybe we could create a wrapper for errors and add more info to have } - var targetManifest string - var tpl bytes.Buffer - tmpl, err := template.New("target"). - Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Target\", \"metadata\": {\"name\": \"target-{{.ExecutionId}}\"}, \"spec\": { \"kubeconfigSecret\" :{\"name\": \"kube-secret-{{.ExecutionId}}\"}} }") + // var targetManifest string + // var tpl bytes.Buffer + // tmpl, err := template.New("target"). + // Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Target\", \"metadata\": {\"name\": \"target-{{.ExecutionId}}\"}, \"spec\": { \"kubeconfigSecret\" :{\"name\": \"kube-secret-{{.ExecutionId}}\"}} }") + // if err != nil { + // fmt.Println("Error creating the template for the target Manifest") + // return nil, err + // } + + // err = tmpl.Execute(&tpl, map[string]string{"ExecutionId":executionId}) + // targetManifest = tpl.String() + + // resp, err := putCDRapiKube( + // *k.Set, + // context, + // "/apis/multicluster.admiralty.io/v1alpha1/namespaces/"+ executionId +"/targets", + // []byte(targetManifest), + // map[string]string{"fieldManager":"kubectl-client-side-apply"}, + // map[string]string{"fieldValidation":"Strict"}, + // ) + + target := map[string]interface{}{ + "apiVersion": "multicluster.admiralty.io/v1alpha1", + "kind": "Target", + "metadata": map[string]interface{}{ + "name": "target-"+executionId, + "namespace": executionId, + }, + "spec": map[string]interface{}{ + "kubeconfigSecret": map[string]string{ + "name" : "kube-secret-"+executionId, + }, + }, + } + + body, err := json.Marshal(target) if err != nil { - fmt.Println("Error creating the template for the target Manifest") + fmt.Println("Error creating the body from the source Manifest") return nil, err } - err = tmpl.Execute(&tpl, map[string]string{"ExecutionId":executionId}) - targetManifest = tpl.String() - resp, err := putCDRapiKube( - *k.Set, - context, - "/apis/multicluster.admiralty.io/v1alpha1/namespaces/"+ executionId +"/targets", - []byte(targetManifest), - map[string]string{"fieldManager":"kubectl-client-side-apply"}, - map[string]string{"fieldValidation":"Strict"}, - ) - + cli, err := NewDynamicClient() if err != nil { - fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", resp) + return nil, errors.New("Could not retrieve dynamic client when creating Admiralty Source : " + err.Error()) + } + + res, err := cli.Resource(gvrTargets). + Namespace(executionId). + Apply(context, + "source-"+executionId, + &unstructured.Unstructured{Object: target}, + metav1.ApplyOptions{ + FieldManager: "kubectl-client-side-apply", + }, + ) + if err != nil { + fmt.Println("Error from k8s API when applying " + string(body) + " to " + gvrSources.String() + " : " , err) + return nil,err + } + + resByte, err := json.Marshal(res) + if err != nil { + // fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", res) return nil, err } - return resp, nil + return resByte, nil } @@ -305,35 +373,78 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context,execut // to rather contact the oc-datacenter from the remote cluster to create the source // locally and retrieve the token for the serviceAccount func (k *KubernetesService) CreateAdmiraltySource(context context.Context,executionId string) ([]byte, error) { - var sourceManifest string - var tpl bytes.Buffer - tmpl, err := template.New("source"). - Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Source\", \"metadata\": {\"name\": \"source-{{.ExecutionId}}\"}, \"spec\": {\"serviceAccountName\": \"sa-{{.ExecutionId}}\"} }") + // var sourceManifest string + // var tpl bytes.Buffer + // tmpl, err := template.New("source"). + // Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Source\", \"metadata\": {\"name\": \"source-{{.ExecutionId}}\"}, \"spec\": {\"serviceAccountName\": \"sa-{{.ExecutionId}}\"} }") + // if err != nil { + // fmt.Println("Error creating the template for the source Manifest") + // return nil, err + // } + + source := map[string]interface{}{ + "apiVersion": "multicluster.admiralty.io/v1alpha1", + "kind": "Source", + "metadata": map[string]interface{}{ + "name": "source-"+executionId, + "namespace": executionId, + }, + "spec": map[string]interface{}{ + "serviceAccountName": "sa-"+executionId, + }, + } + + body, err := json.Marshal(source) if err != nil { - fmt.Println("Error creating the template for the source Manifest") + fmt.Println("Error creating the body from the source Manifest") return nil, err } - err = tmpl.Execute(&tpl, map[string]string{"ExecutionId":executionId}) - sourceManifest = tpl.String() + // err = tmpl.Execute(&tpl, map[string]string{"ExecutionId":executionId}) + // sourceManifest = tpl.String() + // resp, err := putCDRapiKube( + // *k.Set, + // context, + // "/apis/multicluster.admiralty.io/v1alpha1/namespaces/"+ executionId +"/sources", + // []byte(sourceManifest), + // map[string]string{"fieldManager":"kubectl-client-side-apply"}, + // map[string]string{"fieldValidation":"Strict"}, + // ) + + // params := []map[string]string{ + // {"fieldManager":"kubectl-client-side-apply"}, + // {"fieldValidation":"Strict"}, + // } - resp, err := putCDRapiKube( - *k.Set, - context, - "/apis/multicluster.admiralty.io/v1alpha1/namespaces/"+ executionId +"/sources", - []byte(sourceManifest), - map[string]string{"fieldManager":"kubectl-client-side-apply"}, - map[string]string{"fieldValidation":"Strict"}, - ) + cli, err := NewDynamicClient() + if err != nil { + return nil, errors.New("Could not retrieve dynamic client when creating Admiralty Source : " + err.Error()) + } + + + res, err := cli.Resource(gvrSources). + Namespace(executionId). + Apply(context, + "source-"+executionId, + &unstructured.Unstructured{Object: source}, + metav1.ApplyOptions{ + FieldManager: "kubectl-client-side-apply", + }, + ) + if err != nil { + fmt.Println("Error from k8s API when applying " + string(body) + " to " + gvrSources.String() + " : " , err) + return nil,err + } + // We can add more info to the log with the content of resp if not nil - + resByte, err := json.Marshal(res) if err != nil { - fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", resp) + // fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", res) return nil, err } - return resp, nil + return resByte, nil } // Create a secret from a kubeconfing. Use it to create the secret binded to an Admiralty