From a664423842e4aad4ccd491c9390f83b1e781f2c8 Mon Sep 17 00:00:00 2001 From: pb Date: Mon, 30 Jun 2025 12:33:24 +0200 Subject: [PATCH] implemented the /minio/serviceaccount route to create new serviceAccount in the minio corresponding to the parameter, then store it in secret in the namespace corresponding to the executionsId --- controllers/minio.go | 106 ++++++++++++++++++++++++++++++++++- go.mod | 2 +- go.sum | 4 ++ infrastructure/interface.go | 2 + infrastructure/kubernetes.go | 42 ++++++++++++++ infrastructure/minio.go | 5 +- main.go | 2 +- routers/commentsRouter.go | 45 +++++++++++++++ routers/router.go | 5 ++ 9 files changed, 206 insertions(+), 7 deletions(-) diff --git a/controllers/minio.go b/controllers/minio.go index 4f68c6f..604bd3f 100644 --- a/controllers/minio.go +++ b/controllers/minio.go @@ -1,6 +1,12 @@ package controllers -import beego "github.com/beego/beego/v2/server/web" +import ( + "oc-datacenter/infrastructure" + + oclib "cloud.o-forge.io/core/oc-lib" + "cloud.o-forge.io/core/oc-lib/models/live" + beego "github.com/beego/beego/v2/server/web" +) type MinioController struct { beego.Controller @@ -12,11 +18,105 @@ type MinioController struct { // @Success 200 // @Param executions path string true "The executionsID of the execution" // @Param minioId path string true "The ID of the Minio you want to reach" -// @router /minio/:minioId/:executions +// @router /serviceaccount/:minioId/:executions func (m *MinioController) CreateServiceAccount() { + _, peerID, _ := oclib.ExtractTokenInfo(*m.Ctx.Request) + // This part is solely for dev purposes and should be removed once test on + + executionsId := m.Ctx.Input.Param(":executions") minioId := m.Ctx.Input.Param(":minioId") + + // retrieve the live storage with the minioId + s := oclib.NewRequest(oclib.LibDataEnum(oclib.STORAGE_RESOURCE), "", "", []string{}, nil).LoadOne(minioId) + if s.Err != "" { + m.Ctx.Output.SetStatus(400) + m.Data["json"] = map[string]interface{}{"error":s.Err} + m.ServeJSON() + return + } + + live := findLiveStorage(minioId, peerID) + if live == nil { + m.Ctx.Output.SetStatus(404) + m.Data["json"] = map[string]interface{}{"error":"could not find the Minio instance " + s.Err} + m.ServeJSON() + return + } - m.Data["json"] = map[string]interface{}{"exec": executionsId, "minioId": minioId} + url := live.Source + service := infrastructure.NewMinioService(url) + + // call the method ctrating the svcacc + err := service.CreateClient() + if err != nil { + m.Ctx.Output.SetStatus(500) + m.Data["json"] = map[string]interface{}{"error":"could not create the client for " + minioId + " : " + err.Error()} + m.ServeJSON() + return + } + + access, secret, err := service.CreateCredentials(executionsId) + if err != nil { + m.Ctx.Output.SetStatus(500) + m.Data["json"] = map[string]interface{}{"error":"could not create the service account for " + minioId + " : " + err.Error()} + m.ServeJSON() + return + } + + // test if the namespace exists + k, err := infrastructure.NewService() + if err != nil { + m.Ctx.Output.SetStatus(500) + m.Data["json"] = map[string]string{"error": err.Error()} + m.ServeJSON() + return + } + + ns, err := k.GetNamespace(m.Ctx.Request.Context(), executionsId) + if ns == nil { + m.Ctx.Output.SetStatus(403) + m.Data["json"] = map[string]string{"error":"Could not find the namespace corresponding to executionsID " + executionsId} + m.ServeJSON() + return + } + if err != nil { + m.Ctx.Output.SetStatus(500) + m.Data["json"] = map[string]string{"error": "Error when trying to check if namespace " + executionsId + " exists : " + err.Error()} + m.ServeJSON() + return + } + + // store the credentials in the namespace + err = k.CreateSecret(m.Ctx.Request.Context(), minioId, executionsId, access, secret) + if err != nil { + m.Ctx.Output.SetStatus(500) + m.Data["json"] = map[string]string{"error": "Error when storing Minio serviceAccount credentials in namespace " + executionsId + " exists : " + err.Error()} + m.ServeJSON() + return + } + + m.Data["json"] = map[string]string{"access":access,"secret":secret} m.ServeJSON() +} + +func findLiveStorage(storageId string, peerId string) *live.LiveStorage { + res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE),"",peerId,[]string{},nil).LoadAll(false) + if res.Err != "" { + l := oclib.GetLogger() + l.Error().Msg(res.Err) + return nil + } + + for _, dbo := range res.Data { + r := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE),"","",[]string{},nil).LoadOne(dbo.GetID()) + l := r.ToLiveStorage() + for _, id := range l.ResourcesID { + if id == storageId { + return l + } + } + } + + return nil } \ No newline at end of file diff --git a/go.mod b/go.mod index 64804f1..237da11 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.2 toolchain go1.24.4 require ( - cloud.o-forge.io/core/oc-lib v0.0.0-20250624102227-e600fedcab06 + cloud.o-forge.io/core/oc-lib v0.0.0-20250626142041-d58dc5602416 github.com/beego/beego/v2 v2.3.8 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/gorilla/websocket v1.5.3 diff --git a/go.sum b/go.sum index 47fc1f3..ddc27c9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ cloud.o-forge.io/core/oc-lib v0.0.0-20250624102227-e600fedcab06 h1:+RSv62uIC7wsmibsp1XTanQMNznNeOGgPpfhb6ZHT4c= cloud.o-forge.io/core/oc-lib v0.0.0-20250624102227-e600fedcab06/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250626135921-34b7cdcf06b0 h1:FGWOxAAw3Zh19A2FUeAbttsfuFJ3JDli/un9iCu6uD8= +cloud.o-forge.io/core/oc-lib v0.0.0-20250626135921-34b7cdcf06b0/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250626142041-d58dc5602416 h1:C51EHyggOm2QZ7INqhKOiyapTIbtRi8y2eJjVXzdtnM= +cloud.o-forge.io/core/oc-lib v0.0.0-20250626142041-d58dc5602416/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/beego/beego/v2 v2.3.1 h1:7MUKMpJYzOXtCUsTEoXOxsDV/UcHw6CPbaWMlthVNsc= github.com/beego/beego/v2 v2.3.1/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4= diff --git a/infrastructure/interface.go b/infrastructure/interface.go index 469060f..ca68ec8 100644 --- a/infrastructure/interface.go +++ b/infrastructure/interface.go @@ -21,6 +21,8 @@ type Infrastructure interface { GetKubeconfigSecret(context context.Context, executionId string, peerId string) ([]byte, error) CreateAdmiraltyTarget(context context.Context, executionId string, peerId string) ([]byte, error) GetOneNode(context context.Context, executionID string, peerId string) (*v1.Node, error) + GetNamespace(context context.Context, executionID string) (*v1.Namespace, error) + CreateSecret(context context.Context, minioId string, executionID string, access string, secret string) error CheckHealth() error } diff --git a/infrastructure/kubernetes.go b/infrastructure/kubernetes.go index 6b44aea..c470fba 100644 --- a/infrastructure/kubernetes.go +++ b/infrastructure/kubernetes.go @@ -10,6 +10,7 @@ import ( "strings" "time" + oclib "cloud.o-forge.io/core/oc-lib" authv1 "k8s.io/api/authentication/v1" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -26,6 +27,7 @@ import ( 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 } @@ -458,6 +460,21 @@ func (k *KubernetesService) DeleteKubeConfigSecret(executionID string) ([]byte, return []byte{}, nil } +func (k *KubernetesService) GetNamespace(context context.Context, executionID string)(*v1.Namespace,error){ + resp, err := k.Set.CoreV1().Namespaces().Get(context,executionID,metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + return nil, nil + } + if err != nil { + logger := oclib.GetLogger() + logger.Error().Msg("An error occured when trying to get namespace " + executionID + " : " + err.Error()) + return nil, err + } + + return resp, nil +} + + func getCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string) ([]byte, error) { resp, err := client.RESTClient().Get(). AbsPath(path). @@ -566,6 +583,31 @@ func (k *KubernetesService) GetOneNode(context context.Context, executionID stri return nil, nil } +func (k *KubernetesService) CreateSecret(context context.Context, minioId string, executionID string, access string, secret string) error { + + data := map[string][]byte{ + "access-key": []byte(access), + "secret-key": []byte(secret), + } + + s := v1.Secret{ + Type: v1.SecretTypeOpaque, + Data: data, + ObjectMeta: metav1.ObjectMeta{ + Name: minioId+"-secret-s3", + }, + } + + _, err := k.Set.CoreV1().Secrets(executionID).Create(context,&s,metav1.CreateOptions{}) + if err != nil { + logger := oclib.GetLogger() + logger.Error().Msg("An error happened when creating the secret holding minio credentials in namespace " + executionID + " : " + err.Error()) + return err + } + + return nil +} + // Returns a concatenation of the peerId and namespace in order for // kubernetes ressources to have a unique name, under 63 characters // and yet identify which peer they are created for diff --git a/infrastructure/minio.go b/infrastructure/minio.go index 203b2c4..38d5d64 100644 --- a/infrastructure/minio.go +++ b/infrastructure/minio.go @@ -81,18 +81,19 @@ func (m *MinioService) CreateCredentials(executionId string) (string,string,erro return "", "", err } + return res.AccessKey, res.SecretKey, nil } func getRandomCreds() (string, string){ opts := randomstring.GenerationOptions{ - Length: 32, + Length: 20, } a, _ := randomstring.GenerateString(opts) - opts.Length = 64 + opts.Length = 40 s, _ := randomstring.GenerateString(opts) return a,s diff --git a/main.go b/main.go index 26b5166..c759ae9 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,7 @@ func main() { conf.GetConfig().Mode = o.GetStringDefault("MODE", "kubernetes") conf.GetConfig().KubeHost = o.GetStringDefault("KUBERNETES_SERVICE_HOST", os.Getenv("KUBERNETES_SERVICE_HOST")) conf.GetConfig().KubePort = o.GetStringDefault("KUBERNETES_SERVICE_PORT", "6443") - + sDec, err := base64.StdEncoding.DecodeString(o.GetStringDefault("KUBE_CA", "")) if err == nil { conf.GetConfig().KubeCA = string(sDec) diff --git a/routers/commentsRouter.go b/routers/commentsRouter.go index a0854a9..75e6303 100644 --- a/routers/commentsRouter.go +++ b/routers/commentsRouter.go @@ -79,6 +79,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], + beego.ControllerComments{ + Method: "DeleteAdmiraltySession", + Router: `/targets/:execution`, + AllowHTTPMethods: []string{"delete"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], beego.ControllerComments{ Method: "GetAll", @@ -106,6 +115,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], + beego.ControllerComments{ + Method: "Log", + Router: `/:id`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], beego.ControllerComments{ Method: "Put", @@ -124,6 +142,24 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], + beego.ControllerComments{ + Method: "ExtendForExecution", + Router: `/extend/:resource_id/from_execution/:execution_id/to/:duration`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], + beego.ControllerComments{ + Method: "ExtendForNamespace", + Router: `/extend/:resource_id/from_namespace/:namespace/to/:duration`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], beego.ControllerComments{ Method: "Search", @@ -160,6 +196,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:MinioController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:MinioController"], + beego.ControllerComments{ + Method: "CreateServiceAccount", + Router: `/serviceaccount/:minioId/:executions`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["oc-datacenter/controllers:SessionController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:SessionController"], beego.ControllerComments{ Method: "GetToken", diff --git a/routers/router.go b/routers/router.go index 73441aa..842b589 100644 --- a/routers/router.go +++ b/routers/router.go @@ -39,6 +39,11 @@ func init() { &controllers.AdmiraltyController{}, ), ), + beego.NSNamespace("/minio", + beego.NSInclude( + &controllers.MinioController{}, + ), + ), ) beego.AddNamespace(ns)