24 Commits

Author SHA1 Message Date
pb
cb2e4f6028 added the right naming convention to kubeConfigSecret in target 2025-05-15 09:37:35 +02:00
pb
35facf1b74 added :peer to admiralty routes to create peer related resources 2025-05-13 16:33:48 +02:00
pb
24e0137444 shortened how targets are named 2025-05-12 15:00:56 +02:00
pb
ba940bfc80 changed the way kube manifest are applyied 2025-05-12 12:23:38 +02:00
pb
063d57d9e7 updated comments and logs 2025-05-12 12:21:12 +02:00
pb
484c742c31 corrected a typo from a copy/pasted log line 2025-05-06 18:10:53 +02:00
pb
cc3b2a6cfc uncommenting createNamespace method 2025-05-06 18:10:09 +02:00
pb
8e8d0d3e01 added a new parameter to the /admiralty/targets route to specify the peerId of the peer targeted, allowing to name differently peers targeted in a namespace 2025-05-05 16:13:49 +02:00
pb
03f81c66f9 Changed name of the method to create source to be coherent with the one to create target 2025-05-05 15:51:39 +02:00
pb
be721059e5 Merge branch 'main' of https://cloud.o-forge.io/core/oc-datacenter 2025-04-29 11:55:15 +02:00
mr
e4f0f6f4ca Merge branch 'main' of https://cloud.o-forge.io/core/oc-datacenter into main 2025-04-28 14:39:56 +02:00
mr
cf92b46ce6 data 2025-04-28 14:39:53 +02:00
pb
aa42f5f49c debug some typo 2025-04-11 15:45:28 +02:00
pb
98c54eb080 typo when passing gvr for target 2025-04-11 15:34:43 +02:00
pb
afe442d17f refactored the way we apply Source and Target with dynamic client 2025-04-11 15:24:47 +02:00
pb
46b7713404 corrected Apply target 2025-04-11 12:10:54 +02:00
pb
d5ad32e2e4 [NEED REFACTORING] added DynamicClient constructor to make API calls on CDRs 2025-04-11 12:00:21 +02:00
pb
e4ecb8c1db replaced k8s go-client create with apply for the creation of the Secret 2025-04-10 15:48:08 +02:00
pb
cca59faeab when creating source or target returns a 409, don't return an error. Post should be replaced by Put but not working 2025-04-10 14:22:37 +02:00
pb
2cf8923d95 more logs 2025-04-08 10:31:29 +02:00
pb
47ed1b4562 added the label multicluster-scheduler=enabled when creating namespace 2025-04-08 10:31:29 +02:00
pb
063f47c87b Merge branch 'main' of https://cloud.o-forge.io/core/oc-datacenter into feature/admiralty 2025-04-04 18:03:18 +02:00
pb
4bfb16cba6 added node to the returned data when it was found 2025-04-01 11:46:32 +02:00
pb
b08e6a1e70 corrected the options in bee run 2025-03-14 16:58:37 +01:00
11 changed files with 268 additions and 177 deletions

View File

@@ -1,3 +1,5 @@
ARG KUBERNETES_HOST=${KUBERNETES_HOST:-"127.0.0.1"}
FROM golang:alpine AS deps FROM golang:alpine AS deps
WORKDIR /app WORKDIR /app
@@ -28,7 +30,7 @@ RUN export CGO_ENABLED=0 && \
COPY . . COPY . .
RUN sed -i '/replace/d' go.mod RUN sed -i '/replace/d' go.mod
RUN if [ ! -f swagger/index.html ]; then timeout 15 bee run --gendoc=true --downdoc=true; fi RUN if [ ! -f swagger/index.html ]; then timeout 15 bee run -gendoc=true -downdoc=true; fi
RUN bee generate routers RUN bee generate routers
RUN bee generate docs RUN bee generate docs
RUN bee pack RUN bee pack
@@ -39,6 +41,8 @@ RUN sed -i 's/http:\/\/127.0.0.1:8080\/swagger\/swagger.json/swagger.json/g' /ap
FROM golang:alpine FROM golang:alpine
ENV KUBERNETES_SERVICE_HOST=$KUBERNETES_HOST
WORKDIR /app WORKDIR /app
COPY --from=builder /app/extracted/oc-datacenter /usr/bin/ COPY --from=builder /app/extracted/oc-datacenter /usr/bin/
COPY --from=builder /app/extracted/swagger /app/swagger COPY --from=builder /app/extracted/swagger /app/swagger

View File

@@ -10,10 +10,13 @@ import (
"slices" "slices"
"time" "time"
oclib "cloud.o-forge.io/core/oc-lib"
beego "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web"
jwt "github.com/golang-jwt/jwt/v5" jwt "github.com/golang-jwt/jwt/v5"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
) )
type KubeInfo struct { type KubeInfo struct {
@@ -117,12 +120,12 @@ func (c *AdmiraltyController) GetOneTarget() {
c.ServeJSON() c.ServeJSON()
} }
// @Title CreateSource // @Title CreateAdmiraltySource
// @Description Create an Admiralty Source on remote cluster // @Description Create an Admiralty Source on remote cluster
// @Param execution path string true "execution id of the workflow" // @Param execution path string true "execution id of the workflow"
// @Success 201 // @Success 201
// @router /source/:execution [post] // @router /source/:execution [post]
func (c *AdmiraltyController) CreateSource() { func (c *AdmiraltyController) CreateAdmiraltySource() {
execution := c.Ctx.Input.Param(":execution") execution := c.Ctx.Input.Param(":execution")
fmt.Println("execution :: ", execution) fmt.Println("execution :: ", execution)
@@ -139,6 +142,12 @@ func (c *AdmiraltyController) CreateSource() {
res, err := serv.CreateAdmiraltySource(c.Ctx.Request.Context(),execution) res, err := serv.CreateAdmiraltySource(c.Ctx.Request.Context(),execution)
if err != nil { if err != nil {
if apierrors.IsAlreadyExists(err) {
c.Ctx.Output.SetStatus(409)
c.Data["json"] = map[string]string{"info" : "A source already exists for this namespace : " + execution}
c.ServeJSON()
return
}
// change code to 500 // change code to 500
c.Ctx.Output.SetStatus(500) c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()} c.Data["json"] = map[string]string{"error": err.Error()}
@@ -159,13 +168,21 @@ func (c *AdmiraltyController) CreateSource() {
// @Title CreateAdmiraltyTarget // @Title CreateAdmiraltyTarget
// @Description Create an Admiralty Target in the namespace associated to the executionID // @Description Create an Admiralty Target in the namespace associated to the executionID
// @Param execution path string true "execution id of the workflow" // @Param execution path string true "execution id of the workflow"
// @Param peer path string true "peerId of the peer the target points to"
// @Success 201 // @Success 201
// @router /target/:execution [post] // @router /target/:execution/:peer [post]
func (c *AdmiraltyController) CreateAdmiraltyTarget(){ func (c *AdmiraltyController) CreateAdmiraltyTarget(){
var data map[string]interface{} var data map[string]interface{}
execution := c.Ctx.Input.Param(":execution") execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
if execution == "" || peerId == "" {
c.Ctx.Output.SetStatus(400)
c.Data["json"] = map[string]string{"error" : "parameters can be empty " + "execution: " + execution + " peer: " + peerId}
c.ServeJSON()
return
}
serv, err := infrastructure.NewService() serv, err := infrastructure.NewService()
if err != nil { if err != nil {
@@ -176,7 +193,7 @@ func (c *AdmiraltyController) CreateAdmiraltyTarget(){
return return
} }
resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(),execution) resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(),execution, peerId)
if err != nil { if err != nil {
// change code to 500 // change code to 500
c.Ctx.Output.SetStatus(500) c.Ctx.Output.SetStatus(500)
@@ -210,14 +227,16 @@ func (c *AdmiraltyController) CreateAdmiraltyTarget(){
// @Title GetKubeSecret // @Title GetKubeSecret
// @Description Retrieve the secret created from a Kubeconfig that will be associated to an Admiralty Target // @Description Retrieve the secret created from a Kubeconfig that will be associated to an Admiralty Target
// @Param execution path string true "execution id of the workflow" // @Param execution path string true "execution id of the workflow"
// @Param peer path string true "UUID of the peer to which the resource is linked"
// @Success 200 // @Success 200
// @router /secret/:execution [get] // @router /secret/:execution/:peer [get]
func(c *AdmiraltyController) GetKubeSecret() { func(c *AdmiraltyController) GetKubeSecret() {
var data map[string]interface{} var data map[string]interface{}
execution := c.Ctx.Input.Param(":execution") execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
serv, err := infrastructure.NewService() serv, err := infrastructure.NewService()
if err != nil { if err != nil {
@@ -228,7 +247,7 @@ func(c *AdmiraltyController) GetKubeSecret() {
return return
} }
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution) resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution, peerId)
if err != nil { if err != nil {
// change code to 500 // change code to 500
c.Ctx.Output.SetStatus(500) c.Ctx.Output.SetStatus(500)
@@ -260,9 +279,10 @@ func(c *AdmiraltyController) GetKubeSecret() {
// @Description Creat a secret from a Kubeconfig that will be associated to an Admiralty Target // @Description Creat a secret from a Kubeconfig that will be associated to an Admiralty Target
// @Param execution path string true "execution id of the workflow" // @Param execution path string true "execution id of the workflow"
// @Param peer path string true "UUID of the peer to which the resource is linked"
// @Param kubeconfig body controllers.RemoteKubeconfig true "Kubeconfig to use when creating secret" // @Param kubeconfig body controllers.RemoteKubeconfig true "Kubeconfig to use when creating secret"
// @Success 201 // @Success 201
// @router /secret/:execution [post] // @router /secret/:execution/:peer [post]
func (c *AdmiraltyController) CreateKubeSecret() { func (c *AdmiraltyController) CreateKubeSecret() {
var kubeconfig RemoteKubeconfig var kubeconfig RemoteKubeconfig
var respData map[string]interface{} var respData map[string]interface{}
@@ -279,9 +299,8 @@ func (c *AdmiraltyController) CreateKubeSecret() {
return return
} }
execution := c.Ctx.Input.Param(":execution") execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
serv, err := infrastructure.NewService() serv, err := infrastructure.NewService()
if err != nil { if err != nil {
@@ -292,7 +311,7 @@ func (c *AdmiraltyController) CreateKubeSecret() {
return return
} }
resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(),*kubeconfig.Data,execution) resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(),*kubeconfig.Data,execution, peerId)
if err != nil { if err != nil {
// change code to 500 // change code to 500
c.Ctx.Output.SetStatus(500) c.Ctx.Output.SetStatus(500)
@@ -311,13 +330,13 @@ func (c *AdmiraltyController) CreateKubeSecret() {
// @name GetAdmiraltyNodes // @name GetAdmiraltyNodes
// @description Allows user to test if an admiralty connection has already been established : Target and valid Secret set up on the local host and Source set up on remote host // @description Allows user to test if an admiralty connection has already been established : Target and valid Secret set up on the local host and Source set up on remote host
// @Param execution path string true "execution id of the workflow" // @Param execution path string true "execution id of the workflow"
// @Param peer path string true "UUID of the peer to which the resource is linked"
// @Success 200 // @Success 200
// @router /node/:execution [get] // @router /node/:execution/:peer [get]
func (c *AdmiraltyController) GetNodeReady(){ func (c *AdmiraltyController) GetNodeReady(){
var secret v1.Secret var secret v1.Secret
execution := c.Ctx.Input.Param(":execution") execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
serv, err := infrastructure.NewService() serv, err := infrastructure.NewService()
if err != nil { if err != nil {
@@ -339,13 +358,15 @@ func (c *AdmiraltyController) GetNodeReady(){
if node == nil { if node == nil {
c.Ctx.Output.SetStatus(404) c.Ctx.Output.SetStatus(404)
c.Data["json"] = map[string]string{ c.Data["json"] = map[string]string{
"error" : "the node for " + execution + " can't be found, make sure both target and source resources are set up on local and remote hosts", "node" : "the node for " + execution + " can't be found, make sure both target and source resources are set up on local and remote hosts",
} }
c.ServeJSON() c.ServeJSON()
return return
} }
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution)
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution, peerId)
if err != nil { if err != nil {
// change code to 500 // change code to 500
c.Ctx.Output.SetStatus(500) c.Ctx.Output.SetStatus(500)
@@ -359,7 +380,6 @@ func (c *AdmiraltyController) GetNodeReady(){
c.ServeJSON() c.ServeJSON()
return return
} }
// Extract JWT token RS265 encoded // Extract JWT token RS265 encoded
var editedKubeconfig map[string]interface{} var editedKubeconfig map[string]interface{}
@@ -393,14 +413,15 @@ func (c *AdmiraltyController) GetNodeReady(){
} }
if *isExpired { if *isExpired {
c.Data["json"] = map[string]string{ c.Data["json"] = map[string]interface{}{
"token" : "token in the secret is expired and must be regenerated", "token" : "token in the secret is expired and must be regenerated",
"node": node,
} }
c.Ctx.Output.SetStatus(410) c.Ctx.Output.SetStatus(410)
c.ServeJSON() c.ServeJSON()
} }
c.Data["json"] = map[string]bool{"ok": true} c.Data["json"] = map[string]interface{}{"node": node,"token": true}
c.ServeJSON() c.ServeJSON()
} }
@@ -426,6 +447,8 @@ func retrieveTokenFromKonfig(editedKubeconfig map[string]interface{}) (string,er
} }
func isTokenExpired(token string) (*bool, error){ func isTokenExpired(token string) (*bool, error){
logger := oclib.GetLogger()
t, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{}) t, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{})
if err != nil { if err != nil {
fmt.Println("couldn't decode token") fmt.Println("couldn't decode token")
@@ -437,7 +460,11 @@ func isTokenExpired(token string) (*bool, error){
fmt.Println("Error while checking token's expiration time") fmt.Println("Error while checking token's expiration time")
return nil, err return nil, err
} }
fmt.Println("Expiration date : " + expiration.UTC().Format("2006-01-02T15:04:05"))
logger.Debug().Msg("Expiration date : " + expiration.UTC().Format("2006-01-02T15:04:05"))
logger.Debug().Msg(fmt.Sprint("Now : ", time.Now().Unix()))
logger.Debug().Msg(fmt.Sprint("Token : ", expiration.Unix()))
expired := expiration.Unix() < time.Now().Unix() expired := expiration.Unix() < time.Now().Unix()

View File

@@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"oc-datacenter/infrastructure" "oc-datacenter/infrastructure"
"strings"
"time" "time"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
@@ -247,19 +246,11 @@ func (o *BookingController) Post() {
o.ServeJSON() o.ServeJSON()
return return
} }
if err := o.createNamespace(resp.ExecutionsID); err != nil { if err := o.createNamespace(resp.ExecutionsID); err != nil {
if strings.Contains(err.Error(), "already exists") { fmt.Println(err.Error())
err = nil
} else {
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": 500,
"error": err.Error(),
}
o.ServeJSON()
return
}
} }
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": []interface{}{b}, "data": []interface{}{b},
"code": 200, "code": 200,

View File

@@ -3,8 +3,5 @@
"NATS_URL": "nats://localhost:4222", "NATS_URL": "nats://localhost:4222",
"MONGO_DATABASE": "DC_myDC", "MONGO_DATABASE": "DC_myDC",
"KUBERNETES_SERVICE_HOST": "172.16.0.183", "KUBERNETES_SERVICE_HOST": "172.16.0.183",
"port": "8092", "port": "8092"
"KUBE_CA": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTXpnNE5UazJNVFl3SGhjTk1qVXdNakEyTVRZek16TTJXaGNOTXpVd01qQTBNVFl6TXpNMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTXpnNE5UazJNVFl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSbi9jVmNUb1orekZUdWZSL29qbG5JMnVpZXJYeTkxcWhxYWpHdWVobXYKV1A4NVQ1dXpkcE1rcFhrNnB5bTlFU0RlRjk1WDFkeTJqdjVFR3paZzZ2WWtvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXJRK0xUR2NMNXBENnBxSEozaVh5CmZiMFRQUDR3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUlObXp3ejhOUVRCNFlURlZJd3BudDhpQjJ5alRlQjYKbkZxRUN6SWw0amUzQWlFQW04dzRma1h0UEhzUG1Yc0hhUXFGSkhkUm9SQ1pSa016akU3REdZY1lMNVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"KUBE_CERT": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJYVlyeG5xbm54WEl3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOek00T0RVNU5qRTJNQjRYRFRJMU1ESXdOakUyTXpNek5sb1hEVEkyTURJdwpOakUyTXpNek5sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJHeDVVb1Ura01obE9xeHgKTjhRV1FOOGF1ekxXRHpjZTBVbnRYWFdHUmFvWHdHdnlYUldkaFlQcVNoU0xJVGttMG5GV2t5cEZlNUdXTXJlVApZd0hReE9talNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCU0ZlbDVtUXNEaW1vMCtEUzZZZWM1QXdDRXFWREFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQWs3U3UrV3RmQks4SmVPazRreVFVdEFtMkxoak8zV25qOW5SdW9HbVpyTGdDSUJwdVNnNU5oMjUrYm1xMgpZQ2xEM3NLTGdQM1ZKUitCYytxS3h3UjVHbmJwCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTXpnNE5UazJNVFl3SGhjTk1qVXdNakEyTVRZek16TTJXaGNOTXpVd01qQTBNVFl6TXpNMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTXpnNE5UazJNVFl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSTDJSZ1U5RHJZazhKUm4xeDlWSVI3eU5hdWVjaFZuK1pRdDVyeDZaalYKeFRSd0RFT0xXZ1MvbkNpYkp6eUVFNmhLUDVzczBPdnp0ZzlxeFZYU1orNzBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWhYcGVaa0xBNHBxTlBnMHVtSG5PClFNQWhLbFF3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnS09hYVMyczRSWWgrU3J0TXpXTnVtVHduajlKOTZuWUkKL0prdEhjNU5lQnNDSVFDbTY5a1U3cDA5V3hHYWdkNmRQbUlOQ09Fa2V2bzZoQ0dNQTNpd0ZlZ3BiQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"KUBE_DATA": "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU0yYUxXTmtPQ2ZGRTJxM2V1VE9kaHd0RXdxTWRaVUZTTlRPOG50OER0K1RvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFYkhsU2hUNlF5R1U2ckhFM3hCWkEzeHE3TXRZUE54N1JTZTFkZFlaRnFoZkFhL0pkRloyRgpnK3BLRklzaE9TYlNjVmFUS2tWN2taWXl0NU5qQWRERTZRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
} }

View File

@@ -2,6 +2,9 @@ version: '3.4'
services: services:
oc-datacenter: oc-datacenter:
env_file:
- path: ./env.env
required: false
environment: environment:
- MONGO_DATABASE=DC_myDC - MONGO_DATABASE=DC_myDC
image: 'oc-datacenter:latest' image: 'oc-datacenter:latest'

View File

@@ -1,9 +1,5 @@
{ {
"MONGO_URL":"mongodb://mongo:27017/", "MONGO_URL":"mongodb://mongo:27017/",
"NATS_URL":"nats://nats:4222", "NATS_URL":"nats://nats:4222",
"MONGO_DATABASE":"DC_myDC", "MONGO_DATABASE":"DC_myDC"
"KUBERNETES_SERVICE_HOST" : "172.16.0.181",
"KUBE_CA" : "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTXpnNE5UazJNVFl3SGhjTk1qVXdNakEyTVRZek16TTJXaGNOTXpVd01qQTBNVFl6TXpNMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTXpnNE5UazJNVFl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSbi9jVmNUb1orekZUdWZSL29qbG5JMnVpZXJYeTkxcWhxYWpHdWVobXYKV1A4NVQ1dXpkcE1rcFhrNnB5bTlFU0RlRjk1WDFkeTJqdjVFR3paZzZ2WWtvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXJRK0xUR2NMNXBENnBxSEozaVh5CmZiMFRQUDR3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUlObXp3ejhOUVRCNFlURlZJd3BudDhpQjJ5alRlQjYKbkZxRUN6SWw0amUzQWlFQW04dzRma1h0UEhzUG1Yc0hhUXFGSkhkUm9SQ1pSa016akU3REdZY1lMNVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"KUBE_CERT":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJYVlyeG5xbm54WEl3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOek00T0RVNU5qRTJNQjRYRFRJMU1ESXdOakUyTXpNek5sb1hEVEkyTURJdwpOakUyTXpNek5sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJHeDVVb1Ura01obE9xeHgKTjhRV1FOOGF1ekxXRHpjZTBVbnRYWFdHUmFvWHdHdnlYUldkaFlQcVNoU0xJVGttMG5GV2t5cEZlNUdXTXJlVApZd0hReE9talNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCU0ZlbDVtUXNEaW1vMCtEUzZZZWM1QXdDRXFWREFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQWs3U3UrV3RmQks4SmVPazRreVFVdEFtMkxoak8zV25qOW5SdW9HbVpyTGdDSUJwdVNnNU5oMjUrYm1xMgpZQ2xEM3NLTGdQM1ZKUitCYytxS3h3UjVHbmJwCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTXpnNE5UazJNVFl3SGhjTk1qVXdNakEyTVRZek16TTJXaGNOTXpVd01qQTBNVFl6TXpNMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTXpnNE5UazJNVFl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSTDJSZ1U5RHJZazhKUm4xeDlWSVI3eU5hdWVjaFZuK1pRdDVyeDZaalYKeFRSd0RFT0xXZ1MvbkNpYkp6eUVFNmhLUDVzczBPdnp0ZzlxeFZYU1orNzBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWhYcGVaa0xBNHBxTlBnMHVtSG5PClFNQWhLbFF3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnS09hYVMyczRSWWgrU3J0TXpXTnVtVHduajlKOTZuWUkKL0prdEhjNU5lQnNDSVFDbTY5a1U3cDA5V3hHYWdkNmRQbUlOQ09Fa2V2bzZoQ0dNQTNpd0ZlZ3BiQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"KUBE_DATA": "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU0yYUxXTmtPQ2ZGRTJxM2V1VE9kaHd0RXdxTWRaVUZTTlRPOG50OER0K1RvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFYkhsU2hUNlF5R1U2ckhFM3hCWkEzeHE3TXRZUE54N1JTZTFkZFlaRnFoZkFhL0pkRloyRgpnK3BLRklzaE9TYlNjVmFUS2tWN2taWXl0NU5qQWRERTZRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
} }

4
env.env Normal file
View File

@@ -0,0 +1,4 @@
KUBERNETES_SERVICE_HOST=192.168.1.169
KUBE_CA="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTVlk3ZHZhNEdYTVdkMy9jMlhLN3JLYjlnWXgyNSthaEE0NmkyNVBkSFAKRktQL2UxSVMyWVF0dzNYZW1TTUQxaStZdzJSaVppNUQrSVZUamNtNHdhcnFvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWtlUVJpNFJiODduME5yRnZaWjZHClc2SU55NnN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnRXA5ck04WmdNclRZSHYxZjNzOW5DZXZZeWVVa3lZUk4KWjUzazdoaytJS1FDSVFDbk05TnVGKzlTakIzNDFacGZ5ays2NEpWdkpSM3BhcmVaejdMd2lhNm9kdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
KUBE_CERT="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJWUxWNkFPQkdrU1F3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekl6TVRFeU1ETTJNQjRYRFRJME1EZ3dPREV3TVRNMU5sb1hEVEkxTURndwpPREV3TVRNMU5sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJGQ2Q1MFdPeWdlQ2syQzcKV2FrOWY4MVAvSkJieVRIajRWOXBsTEo0ck5HeHFtSjJOb2xROFYxdUx5RjBtOTQ2Nkc0RmRDQ2dqaXFVSk92Swp3NVRPNnd5alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCVFJkOFI5cXVWK2pjeUVmL0ovT1hQSzMyS09XekFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQTArbThqTDBJVldvUTZ0dnB4cFo4NVlMalF1SmpwdXM0aDdnSXRxS3NmUVVDSUI2M2ZNdzFBMm5OVWU1TgpIUGZOcEQwSEtwcVN0Wnk4djIyVzliYlJUNklZCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFRc3hXWk9pbnIrcVp4TmFEQjVGMGsvTDF5cE01VHAxOFRaeU92ektJazQKRTFsZWVqUm9STW0zNmhPeVljbnN3d3JoNnhSUnBpMW5RdGhyMzg0S0Z6MlBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBYZkVmYXJsZm8zTWhIL3lmemx6Cnl0OWlqbHN3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUxJL2dNYnNMT3MvUUpJa3U2WHVpRVMwTEE2cEJHMXgKcnBlTnpGdlZOekZsQWlFQW1wdjBubjZqN3M0MVI0QzFNMEpSL0djNE53MHdldlFmZWdEVGF1R2p3cFk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
KUBE_DATA="LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5ZS1BFb1dhd1NKUzJlRW5oWmlYMk5VZlY1ZlhKV2krSVNnV09TNFE5VTlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVUozblJZN0tCNEtUWUx0WnFUMS96VS84a0Z2Sk1lUGhYMm1Vc25pczBiR3FZblkyaVZEeApYVzR2SVhTYjNqcm9iZ1YwSUtDT0twUWs2OHJEbE03ckRBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="

View File

@@ -16,11 +16,11 @@ type Infrastructure interface {
CreateRoleBinding(ctx context.Context, ns string, roleBinding string, role string) error CreateRoleBinding(ctx context.Context, ns string, roleBinding string, role string) error
CreateRole(ctx context.Context, ns string, role string, groups [][]string, resources [][]string, verbs [][]string) error CreateRole(ctx context.Context, ns string, role string, groups [][]string, resources [][]string, verbs [][]string) error
GetTargets(ctx context.Context) ([]string,error) GetTargets(ctx context.Context) ([]string,error)
CreateAdmiraltySource(context context.Context,executionId string) ([]byte, error) CreateAdmiraltySource(context context.Context, executionId string) ([]byte, error)
CreateKubeconfigSecret(context context.Context,kubeconfig string, executionId string) ([]byte, error) CreateKubeconfigSecret(context context.Context, kubeconfig string, executionId string, peerId string) ([]byte, error)
GetKubeconfigSecret(context context.Context,executionId string) ([]byte, error) GetKubeconfigSecret(context context.Context, executionId string, peerId string) ([]byte, error)
CreateAdmiraltyTarget(context context.Context,executionId string)([]byte,error) CreateAdmiraltyTarget(context context.Context, executionId string, peerId string)([]byte,error)
GetOneNode(context context.Context,executionID string) (*v1.Node, error) GetOneNode(context context.Context, executionID string) (*v1.Node, error)
} }
var _service = map[string]func() (Infrastructure, error){ var _service = map[string]func() (Infrastructure, error){

View File

@@ -1,13 +1,11 @@
package infrastructure package infrastructure
import ( import (
"bytes"
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"html/template"
"oc-datacenter/conf" "oc-datacenter/conf"
"strings" "strings"
@@ -16,12 +14,40 @@ import (
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 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/kubernetes"
"k8s.io/client-go/rest" "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 { 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) { func NewKubernetesService() (Infrastructure, error) {
@@ -33,6 +59,7 @@ func NewKubernetesService() (Infrastructure, error) {
KeyData: []byte(conf.GetConfig().KubeData), KeyData: []byte(conf.GetConfig().KubeData),
}, },
} }
// Create clientset // Create clientset
clientset, err := kubernetes.NewForConfig(config) clientset, err := kubernetes.NewForConfig(config)
fmt.Println("NewForConfig", clientset, err) fmt.Println("NewForConfig", clientset, err)
@@ -43,6 +70,7 @@ func NewKubernetesService() (Infrastructure, error) {
return nil, errors.New("Error creating Kubernetes client: clientset is nil") return nil, errors.New("Error creating Kubernetes client: clientset is nil")
} }
return &KubernetesService{ return &KubernetesService{
Set: clientset, Set: clientset,
}, nil }, nil
@@ -82,6 +110,9 @@ func (k *KubernetesService) CreateNamespace(ctx context.Context, ns string) erro
namespace := &v1.Namespace{ namespace := &v1.Namespace{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: ns, Name: ns,
Labels: map[string]string{
"multicluster-scheduler":"enabled",
},
}, },
} }
// Create the namespace // Create the namespace
@@ -195,27 +226,25 @@ func (k *KubernetesService) GenerateToken(ctx context.Context, ns string, durati
return token.Status.Token, nil return token.Status.Token, nil
} }
// Needs refactoring : // Needs refactoring :
// - Retrieving the metada (in a method that Unmarshall the part of the json in a metadata object) // - Retrieving the metada (in a method that Unmarshall the part of the json in a metadata object)
func (k *KubernetesService) GetTargets(ctx context.Context) ([]string,error){ func (k *KubernetesService) GetTargets(ctx context.Context) ([]string, error) {
var listTargets []string var listTargets []string
resp, err := getCDRapiKube(*k.Set, ctx,"/apis/multicluster.admiralty.io/v1alpha1/targets") resp, err := getCDRapiKube(*k.Set, ctx, "/apis/multicluster.admiralty.io/v1alpha1/targets")
if err != nil { if err != nil {
return nil,err return nil, err
} }
fmt.Println(string(resp)) fmt.Println(string(resp))
var targetDict map[string]interface{} var targetDict map[string]interface{}
err = json.Unmarshal(resp,&targetDict) err = json.Unmarshal(resp, &targetDict)
if err != nil { if err != nil {
fmt.Println("TODO: handle the error when unmarshalling k8s API response") fmt.Println("TODO: handle the error when unmarshalling k8s API response")
return nil, err return nil, err
} }
b, _ := json.MarshalIndent(targetDict,""," ") b, _ := json.MarshalIndent(targetDict, "", " ")
fmt.Println(string(b)) fmt.Println(string(b))
data := targetDict["items"].([]interface{}) data := targetDict["items"].([]interface{})
@@ -224,25 +253,25 @@ func (k *KubernetesService) GetTargets(ctx context.Context) ([]string,error){
var metadata metav1.ObjectMeta var metadata metav1.ObjectMeta
item := item.(map[string]interface{}) item := item.(map[string]interface{})
byteMetada, err := json.Marshal(item["metadata"]) byteMetada, err := json.Marshal(item["metadata"])
if err != nil { if err != nil {
fmt.Println("Error while Marshalling metadata field") fmt.Println("Error while Marshalling metadata field")
return nil,err return nil, err
} }
err = json.Unmarshal(byteMetada,&metadata) err = json.Unmarshal(byteMetada, &metadata)
if err != nil { if err != nil {
fmt.Println("Error while Unmarshalling metadata field to the library object") fmt.Println("Error while Unmarshalling metadata field to the library object")
return nil,err return nil, err
} }
listTargets = append(listTargets, metadata.Name) listTargets = append(listTargets, metadata.Name)
} }
return listTargets,nil return listTargets, nil
} }
// Admiralty Target allows a cluster to deploy pods to remote cluster // Admiralty Target allows a cluster to deploy pods to remote cluster
// //
// The remote cluster must : // The remote cluster must :
// //
@@ -250,48 +279,46 @@ func (k *KubernetesService) GetTargets(ctx context.Context) ([]string,error){
// //
// - have declared the same namespace as the one where the pods are created in the local cluster // - have declared the same namespace as the one where the pods are created in the local cluster
// //
// - have delcared a serviceAccount with sufficient permission to create pods // - have delcared a serviceAccount with sufficient permission to create pods
func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context,executionId string)([]byte,error){ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context, executionId string, peerId string) ([]byte, error) {
exists, err := k.GetKubeconfigSecret(context,executionId) exists, err := k.GetKubeconfigSecret(context, executionId, peerId)
if err != nil { if err != nil {
fmt.Println("Error verifying kube-secret before creating target") fmt.Println("Error verifying kube-secret before creating target")
return nil, err return nil, err
} }
if exists == nil { if exists == nil {
fmt.Println("Target needs to be binded to a secret in namespace ",executionId) fmt.Println("Target needs to be binded to a secret in namespace ", executionId)
return nil, nil // Maybe we could create a wrapper for errors and add more info to have return nil, nil // Maybe we could create a wrapper for errors and add more info to have
} }
var targetManifest string targetName := "target-" + getConcatenatedName(peerId,executionId)
var tpl bytes.Buffer target := map[string]interface{}{
tmpl, err := template.New("target"). "apiVersion": "multicluster.admiralty.io/v1alpha1",
Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Target\", \"metadata\": {\"name\": \"target-{{.ExecutionId}}\"}, \"spec\": { \"kubeconfigSecret\" :{\"name\": \"kube-secret-{{.ExecutionId}}\"}} }") "kind": "Target",
"metadata": map[string]interface{}{
"name": targetName,
"namespace": executionId,
"labels": map[string]interface{}{
"peer": peerId,
},
},
"spec": map[string]interface{}{
"kubeconfigSecret": map[string]string{
"name" : "kube-secret-"+ getConcatenatedName(peerId, executionId),
},
},
}
res, err := dynamicClientApply(executionId, targetName, gvrTargets, context, target)
if err != nil { if err != nil {
fmt.Println("Error creating the template for the target Manifest") return nil, errors.New("Error when trying to apply Target definition :" + err.Error())
return nil, err
} }
err = tmpl.Execute(&tpl, map[string]string{"ExecutionId":executionId}) return res, nil
targetManifest = tpl.String()
resp, err := postCDRapiKube(
*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"},
)
if err != nil {
fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", resp)
return nil, err
}
return resp, nil
} }
// Admiralty Source allows a cluster to receive pods from a remote cluster // Admiralty Source allows a cluster to receive pods from a remote cluster
// //
// The source must be associated to a serviceAccount, which will execute the pods locally. // The source must be associated to a serviceAccount, which will execute the pods locally.
@@ -301,39 +328,31 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context,execut
// to rather contact the oc-datacenter from the remote cluster to create the source // to rather contact the oc-datacenter from the remote cluster to create the source
// locally and retrieve the token for the serviceAccount // locally and retrieve the token for the serviceAccount
func (k *KubernetesService) CreateAdmiraltySource(context context.Context,executionId string) ([]byte, error) { func (k *KubernetesService) CreateAdmiraltySource(context context.Context,executionId string) ([]byte, error) {
var sourceManifest string
var tpl bytes.Buffer source := map[string]interface{}{
tmpl, err := template.New("source"). "apiVersion": "multicluster.admiralty.io/v1alpha1",
Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Source\", \"metadata\": {\"name\": \"source-{{.ExecutionId}}\"}, \"spec\": {\"serviceAccountName\": \"sa-{{.ExecutionId}}\"} }") "kind": "Source",
"metadata": map[string]interface{}{
"name": "source-"+executionId,
"namespace": executionId,
},
"spec": map[string]interface{}{
"serviceAccountName": "sa-"+executionId,
},
}
res, err := dynamicClientApply(executionId, "source-" + executionId,gvrSources, context, source)
if err != nil { if err != nil {
fmt.Println("Error creating the template for the source Manifest") return nil, errors.New("Error when trying to apply Source definition :" + err.Error())
return nil, err
} }
err = tmpl.Execute(&tpl, map[string]string{"ExecutionId":executionId}) return res, nil
sourceManifest = tpl.String()
resp, err := postCDRapiKube(
*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"},
)
// We can add more info to the log with the content of resp if not nil
if err != nil {
fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", resp)
return nil, err
}
return resp, nil
} }
// Create a secret from a kubeconfing. Use it to create the secret binded to an Admiralty // Create a secret from a kubeconfing. Use it to create the secret binded to an Admiralty
// target, which must contain the serviceAccount's token value // target, which must contain the serviceAccount's token value
func (k *KubernetesService) CreateKubeconfigSecret(context context.Context,kubeconfig string, executionId string) ([]byte, error) { func (k *KubernetesService) CreateKubeconfigSecret(context context.Context, kubeconfig string, executionId string, peerId string) ([]byte, error) {
config, err := base64.StdEncoding.DecodeString(kubeconfig) config, err := base64.StdEncoding.DecodeString(kubeconfig)
// config, err := base64.RawStdEncoding.DecodeString(kubeconfig) // config, err := base64.RawStdEncoding.DecodeString(kubeconfig)
if err != nil { if err != nil {
@@ -342,35 +361,40 @@ func (k *KubernetesService) CreateKubeconfigSecret(context context.Context,kubec
return nil, err return nil, err
} }
secretManifest := &v1.Secret{ secretApplyConfig := apply.Secret("kube-secret-" + getConcatenatedName(peerId, executionId),
ObjectMeta: metav1.ObjectMeta{ executionId).
Name: "kube-secret-" + executionId, WithData(map[string][]byte{
Namespace: executionId, "config": config,
}, },
Data: map[string][]byte{ )
"config": config,
},
}
// exists, err := k.GetKubeconfigSecret(context,executionId)
// if err != nil {
// fmt.Println("Error verifying if kube secret exists in namespace ", executionId)
// return nil, err
// }
// if exists != nil {
// fmt.Println("kube-secret already exists in namespace", executionId)
// fmt.Println("Overriding existing kube-secret with a newer resource")
// // TODO : implement DeleteKubeConfigSecret(executionID)
// deleted, err := k.DeleteKubeConfigSecret(executionId)
// _ = deleted
// _ = err
// }
exists, err := k.GetKubeconfigSecret(context,executionId)
if err != nil {
fmt.Println("Error verifying if kube secret exists in namespace ", executionId)
return nil, err
}
if exists != nil {
fmt.Println("kube-secret already exists in namespace", executionId)
fmt.Println("Overriding existing kube-secret with a newer resource")
// TODO : implement DeleteKubeConfigSecret(executionID)
deleted, err := k.DeleteKubeConfigSecret(executionId)
_ = deleted
_ = err
}
resp, err := k.Set.CoreV1(). resp, err := k.Set.CoreV1().
Secrets(executionId). Secrets(executionId).
Create(context,secretManifest,metav1.CreateOptions{}) Apply(context,
secretApplyConfig,
metav1.ApplyOptions{
FieldManager: "admiralty-manager",
})
if err != nil { if err != nil {
fmt.Println("Error while trying to contact API to get secret kube-secret-"+executionId) fmt.Println("Error while trying to contact API to get secret kube-secret-" + executionId)
fmt.Println(err) fmt.Println(err)
return nil, err return nil, err
} }
@@ -382,19 +406,19 @@ func (k *KubernetesService) CreateKubeconfigSecret(context context.Context,kubec
return nil, err return nil, err
} }
return data, nil return data, nil
} }
func (k *KubernetesService) GetKubeconfigSecret(context context.Context,executionId string) ([]byte, error) { func (k *KubernetesService) GetKubeconfigSecret(context context.Context, executionId string, peerId string) ([]byte, error) {
resp, err := k.Set.CoreV1(). resp, err := k.Set.CoreV1().
Secrets(executionId). Secrets(executionId).
Get(context,"kube-secret-"+executionId,metav1.GetOptions{}) Get(context, "kube-secret-"+ getConcatenatedName(peerId, executionId), metav1.GetOptions{})
if err != nil { if err != nil {
if(apierrors.IsNotFound(err)){ if apierrors.IsNotFound(err) {
fmt.Println("kube-secret not found for execution", executionId) fmt.Println("kube-secret not found for execution", executionId)
return nil, nil return nil, nil
} }
fmt.Println("Error while trying to contact API to get secret kube-secret-"+executionId) fmt.Println("Error while trying to contact API to get secret kube-secret-" + executionId)
fmt.Println(err) fmt.Println(err)
return nil, err return nil, err
} }
@@ -410,41 +434,74 @@ func (k *KubernetesService) GetKubeconfigSecret(context context.Context,executio
return data, nil return data, nil
} }
func (k *KubernetesService) DeleteKubeConfigSecret(executionID string) ([]byte, error){ func (k *KubernetesService) DeleteKubeConfigSecret(executionID string) ([]byte, error) {
return []byte{}, nil return []byte{}, nil
} }
func getCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string) ([]byte,error) { func getCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string) ([]byte, error) {
resp, err := client.RESTClient().Get(). resp, err := client.RESTClient().Get().
AbsPath(path). AbsPath(path).
DoRaw(ctx) // from https://stackoverflow.com/questions/60764908/how-to-access-kubernetes-crd-using-client-go DoRaw(ctx) // from https://stackoverflow.com/questions/60764908/how-to-access-kubernetes-crd-using-client-go
if err != nil { if err != nil {
fmt.Println("Error from k8s API when getting " + path + " : " , err) fmt.Println("Error from k8s API when getting "+path+" : ", err)
return nil,err return nil, err
} }
return resp, nil return resp, nil
} }
func postCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string, body []byte, params ...map[string]string) ([]byte, error){ func dynamicClientApply(executionId string, resourceName string, resourceDefinition schema.GroupVersionResource, ctx context.Context, object map[string]interface{}) ([]byte, error) {
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(resourceDefinition).
Namespace(executionId).
Apply(ctx,
resourceName,
&unstructured.Unstructured{Object: object},
metav1.ApplyOptions{
FieldManager: "kubectl-client-side-apply",
},
)
if err != nil {
o, err := json.Marshal(object)
fmt.Println("Error from k8s API when applying " + fmt.Sprint(string(o)) + " 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 , " : ", res)
return nil, err
}
return resByte, nil
}
func putCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string, body []byte, params ...map[string]string) ([]byte, error){
req := client.RESTClient(). req := client.RESTClient().
Post(). Post().
AbsPath(path). AbsPath(path).
Body(body) Body(body)
for _, param := range params { for _, param := range params {
for k,v := range param { for k, v := range param {
req = req.Param(k,v) req = req.Param(k, v)
} }
} }
resp, err := req.DoRaw(ctx) resp, err := req.DoRaw(ctx)
if err != nil { if err != nil {
fmt.Println("Error from k8s API when posting " + string(body) + " to " + path + " : " , err) fmt.Println("Error from k8s API when posting "+string(body)+" to "+path+" : ", err)
return nil,err return nil, err
} }
return resp, nil return resp, nil
@@ -453,13 +510,13 @@ func postCDRapiKube(client kubernetes.Clientset, ctx context.Context, path strin
// Returns the Kubernetes' Node object corresponding to the executionID if it exists on this host // Returns the Kubernetes' Node object corresponding to the executionID if it exists on this host
// //
// The node is created when an admiralty Target (on host) can connect to an admiralty Source (on remote) // The node is created when an admiralty Target (on host) can connect to an admiralty Source (on remote)
func (k *KubernetesService) GetOneNode(context context.Context,executionID string) (*v1.Node, error) { func (k *KubernetesService) GetOneNode(context context.Context, executionID string) (*v1.Node, error) {
res, err := k.Set.CoreV1(). res, err := k.Set.CoreV1().
Nodes(). Nodes().
List( List(
context, context,
metav1.ListOptions{}, metav1.ListOptions{},
) )
if err != nil { if err != nil {
fmt.Println("Error getting the list of nodes from k8s API") fmt.Println("Error getting the list of nodes from k8s API")
fmt.Println(err) fmt.Println(err)
@@ -467,10 +524,21 @@ func (k *KubernetesService) GetOneNode(context context.Context,executionID strin
} }
for _, node := range res.Items { for _, node := range res.Items {
if isNode := strings.Contains(node.Name,"admiralty-"+executionID+"-target-"+executionID+"-"); isNode { if isNode := strings.Contains(node.Name, "admiralty-"+executionID+"-target-"+executionID+"-"); isNode {
return &node, nil return &node, nil
} }
} }
return nil, nil return nil, 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
func getConcatenatedName(peerId string, namespace string) string {
s := strings.Split(peerId, "-")[:2]
p := s[0] + "-" + s[1]
return p + "-" + namespace
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"oc-datacenter/conf" "oc-datacenter/conf"
_ "oc-datacenter/routers" _ "oc-datacenter/routers"
"os"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
@@ -20,7 +21,7 @@ func main() {
// Load the right config file // Load the right config file
o := oclib.GetConfLoader() o := oclib.GetConfLoader()
conf.GetConfig().Mode = o.GetStringDefault("MODE", "kubernetes") conf.GetConfig().Mode = o.GetStringDefault("MODE", "kubernetes")
conf.GetConfig().KubeHost = o.GetStringDefault("KUBERNETES_SERVICE_HOST", "") conf.GetConfig().KubeHost = o.GetStringDefault("KUBERNETES_SERVICE_HOST", os.Getenv("KUBERNETES_SERVICE_HOST"))
conf.GetConfig().KubePort = o.GetStringDefault("KUBERNETES_SERVICE_PORT", "6443") conf.GetConfig().KubePort = o.GetStringDefault("KUBERNETES_SERVICE_PORT", "6443")
sDec, err := base64.StdEncoding.DecodeString(o.GetStringDefault("KUBE_CA", "")) sDec, err := base64.StdEncoding.DecodeString(o.GetStringDefault("KUBE_CA", ""))

View File

@@ -19,7 +19,7 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "GetNodeReady", Method: "GetNodeReady",
Router: `/node/:execution`, Router: `/node/:execution/:peer`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
@@ -28,7 +28,7 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "GetKubeSecret", Method: "GetKubeSecret",
Router: `/secret/:execution`, Router: `/secret/:execution/:peer`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
@@ -37,7 +37,7 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "CreateKubeSecret", Method: "CreateKubeSecret",
Router: `/secret/:execution`, Router: `/secret/:execution/:peer`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
@@ -45,7 +45,7 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "CreateSource", Method: "CreateAdmiraltySource",
Router: `/source/:execution`, Router: `/source/:execution`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(), MethodParams: param.Make(),
@@ -55,7 +55,7 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "CreateAdmiraltyTarget", Method: "CreateAdmiraltyTarget",
Router: `/target/:execution`, Router: `/target/:execution/:peer`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,