512 lines
13 KiB
Go
512 lines
13 KiB
Go
package controllers
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"oc-datacenter/conf"
|
|
"oc-datacenter/infrastructure"
|
|
"oc-datacenter/models"
|
|
"slices"
|
|
"time"
|
|
|
|
beego "github.com/beego/beego/v2/server/web"
|
|
jwt "github.com/golang-jwt/jwt/v5"
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/util/yaml"
|
|
)
|
|
|
|
type KubeInfo struct {
|
|
Url *string
|
|
KubeCA *string
|
|
KubeCert *string
|
|
KubeKey *string
|
|
}
|
|
|
|
type RemoteKubeconfig struct {
|
|
Data *string
|
|
}
|
|
|
|
type KubeUser struct {
|
|
Name string
|
|
User struct {
|
|
Token string
|
|
}
|
|
}
|
|
|
|
type KubeconfigToken struct {
|
|
ApiVersion string `yaml:"apiVersion"`
|
|
Kind string `yaml:"kind"`
|
|
Preferences string `yaml:"preferences"`
|
|
CurrentContext string `yaml:"current-context"`
|
|
Clusters []struct{
|
|
Cluster struct{
|
|
CA string `yaml:"certificate-authority-data"`
|
|
Server string `yaml:"server"`
|
|
|
|
} `yaml:"cluster"`
|
|
Name string `yaml:"name"`
|
|
} `yaml:"clusters"`
|
|
Contexts []struct{
|
|
Context struct{
|
|
Cluster string `yaml:"cluster"`
|
|
User string `yaml:"user"`
|
|
} `yaml:"context"`
|
|
Name string `yaml:"name"`
|
|
} `yaml:"contexts"`
|
|
Users []struct{
|
|
Name string `yaml:"name"`
|
|
User struct {
|
|
Token string `yaml:"token"`
|
|
} `yaml:"user"`
|
|
} `yaml:"users"`
|
|
}
|
|
|
|
// Operations about the admiralty objects of the datacenter
|
|
type AdmiraltyController struct {
|
|
beego.Controller
|
|
}
|
|
|
|
// @Title GetAllTargets
|
|
// @Description find all Admiralty Target
|
|
// @Success 200
|
|
// @router /targets [get]
|
|
func (c *AdmiraltyController) GetAllTargets() {
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.ServeJSON()
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
return
|
|
}
|
|
|
|
res, err := serv.GetTargets(c.Ctx.Request.Context())
|
|
c.Data["json"] = res
|
|
c.ServeJSON()
|
|
}
|
|
|
|
// @Title GetOneTarget
|
|
// @Description find one Admiralty Target
|
|
// @Param id path string true "the name of the target to get"
|
|
// @Success 200
|
|
// @router /targets/:id [get]
|
|
func (c *AdmiraltyController) GetOneTarget() {
|
|
id := c.Ctx.Input.Param(":id")
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.ServeJSON()
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
return
|
|
}
|
|
|
|
res, err := serv.GetTargets(c.Ctx.Request.Context())
|
|
id = "target-"+id
|
|
found := slices.Contains(res,id)
|
|
if !found {
|
|
c.Ctx.Output.SetStatus(404)
|
|
c.ServeJSON()
|
|
}
|
|
|
|
c.Data["json"] = id
|
|
c.ServeJSON()
|
|
}
|
|
|
|
// @Title CreateSource
|
|
// @Description Create an Admiralty Source on remote cluster
|
|
// @Param dc_id path string true "which dc to contact"
|
|
// @Param execution path string true "execution id of the workflow"
|
|
// @Param kubeconfigInfo body controllers.KubeInfo true "url and serviceAccount to use with the source formatted as json object"
|
|
// @Success 200
|
|
// @router /source/:dc_id/:execution [post]
|
|
func (c *AdmiraltyController) CreateSource() {
|
|
var data KubeInfo
|
|
json.Unmarshal(c.Ctx.Input.CopyBody(10000000),&data)
|
|
if data.Url == nil || data.KubeCA == nil || data.KubeCert == nil|| data.KubeKey == nil {
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.ServeJSON()
|
|
missingData := fmt.Sprint(data)
|
|
c.Data["json"] = map[string]string{"error" : "Missing something in " + missingData}
|
|
c.ServeJSON()
|
|
}
|
|
fmt.Println("")
|
|
fmt.Println("URL : %v", data.Url)
|
|
fmt.Println("")
|
|
fmt.Println("CA : %v", data.KubeCA)
|
|
fmt.Println("")
|
|
fmt.Println("Key : ", data.KubeKey)
|
|
|
|
dc_id := c.Ctx.Input.Param(":dc_id")
|
|
execution := c.Ctx.Input.Param(":execution")
|
|
_ = dc_id
|
|
serv, err := infrastructure.NewRemoteKubernetesService(
|
|
*data.Url,
|
|
*data.KubeCA,
|
|
*data.KubeCert,
|
|
*data.KubeKey,
|
|
)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.ServeJSON()
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
res, err := serv.CreateAdmiraltySource(c.Ctx.Request.Context(),execution)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
// TODO : Return a description of the created resource
|
|
var respData map[string]interface{}
|
|
err = json.Unmarshal(res,&respData)
|
|
c.Data["json"] = respData
|
|
c.ServeJSON()
|
|
|
|
}
|
|
|
|
// @Title CreateAdmiraltyTarget
|
|
// @Description Create an Admiralty Target in the namespace associated to the executionID
|
|
// @Param dc_id path string true "which dc to contact"
|
|
// @Param execution path string true "execution id of the workflow"
|
|
// @Success 201
|
|
// @router /target/:dc_id/:execution [post]
|
|
func (c *AdmiraltyController) CreateAdmiraltyTarget(){
|
|
var data map[string]interface{}
|
|
dc_id := c.Ctx.Input.Param(":dc_id")
|
|
execution := c.Ctx.Input.Param(":execution")
|
|
_ = dc_id
|
|
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(),execution)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
if resp == nil {
|
|
fmt.Println("Error while trying to create Admiralty target")
|
|
fmt.Println(resp)
|
|
fmt.Println(err)
|
|
c.Ctx.Output.SetStatus(401)
|
|
c.Data["json"] = map[string]string{"error" : "Could not perform the action" }
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(resp,&data)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.ServeJSON()
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
return
|
|
}
|
|
c.Data["json"] = data
|
|
c.ServeJSON()
|
|
|
|
}
|
|
|
|
// @Title GetKubeSecret
|
|
// @Description Retrieve the secret created from a Kubeconfig that will be associated to an Admiralty Target
|
|
// @Param dc_id path string true "which dc to contact"
|
|
// @Param execution path string true "execution id of the workflow"
|
|
// @Success 200
|
|
// @router /secret/:dc_id/:execution [get]
|
|
func(c *AdmiraltyController) GetKubeSecret() {
|
|
var data map[string]interface{}
|
|
dc_id := c.Ctx.Input.Param(":dc_id")
|
|
execution := c.Ctx.Input.Param(":execution")
|
|
_ = dc_id
|
|
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
if resp == nil {
|
|
c.Ctx.Output.SetStatus(404)
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(resp,&data)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.ServeJSON()
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = data
|
|
c.ServeJSON()
|
|
}
|
|
|
|
|
|
// @Title CreateKubeSecret
|
|
// @Description Creat a secret from a Kubeconfig that will be associated to an Admiralty Target
|
|
// @Param dc_id path string true "which dc to contact"
|
|
// @Param execution path string true "execution id of the workflow"
|
|
// @Param kubeconfig body controllers.Kubeconfig true "Kubeconfig to use when creating secret"
|
|
// @Success 200
|
|
// @router /secret/:dc_id/:execution [post]
|
|
func (c *AdmiraltyController) CreateKubeSecret() {
|
|
var kubeconfig RemoteKubeconfig
|
|
var respData map[string]interface{}
|
|
|
|
data := c.Ctx.Input.CopyBody(100000)
|
|
|
|
err := json.Unmarshal(data, &kubeconfig)
|
|
if err != nil {
|
|
fmt.Println("Error when retrieving the data for kubeconfig from request")
|
|
fmt.Println(err)
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
dc_id := c.Ctx.Input.Param(":dc_id")
|
|
execution := c.Ctx.Input.Param(":execution")
|
|
_ = dc_id
|
|
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(),*kubeconfig.Data,execution)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(resp,&respData)
|
|
c.Data["json"] = respData
|
|
c.ServeJSON()
|
|
|
|
}
|
|
|
|
// @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
|
|
// @Param dc_id path string true "which dc to contact"
|
|
// @Param execution path string true "execution id of the workflow"
|
|
// @Success 200
|
|
// @Success 203
|
|
// @router /node/:dc_id/:execution [get]
|
|
func (c *AdmiraltyController) GetNodeReady(){
|
|
var secret v1.Secret
|
|
dc_id := c.Ctx.Input.Param(":dc_id")
|
|
execution := c.Ctx.Input.Param(":execution")
|
|
_ = dc_id
|
|
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
node, err := serv.GetOneNode(c.Ctx.Request.Context(),execution)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
if node == nil {
|
|
c.Ctx.Output.SetStatus(404)
|
|
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",
|
|
}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution)
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
if resp == nil {
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": "Nodes was up but the secret can't be found"}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
|
|
// Extract JWT token RS265 encoded
|
|
var editedKubeconfig map[string]interface{}
|
|
var kubeUsers []KubeUser
|
|
json.Unmarshal(resp,&secret)
|
|
byteEditedKubeconfig := secret.Data["config"]
|
|
err = yaml.Unmarshal(byteEditedKubeconfig,&editedKubeconfig)
|
|
// err = json.Unmarshal(byteEditedKubeconfig,&editedKubeconfig)
|
|
if err != nil {
|
|
fmt.Println("Error while retrieving the kubeconfig from secret-",execution)
|
|
fmt.Println(err)
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = err
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
b, err := json.Marshal(editedKubeconfig["users"])
|
|
err = yaml.Unmarshal(b,&kubeUsers)
|
|
token := kubeUsers[0].User.Token
|
|
|
|
// Decode token
|
|
t, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{})
|
|
if err != nil {
|
|
fmt.Println("couldn't decode token")
|
|
c.Data["json"] = false
|
|
c.ServeJSON()
|
|
}
|
|
|
|
expiration, err := t.Claims.GetExpirationTime()
|
|
fmt.Println("Expiration date : " + expiration.UTC().Format("2006-01-02T15:04:05"))
|
|
|
|
if expiration.Add(1 * time.Hour).Unix() < time.Now().Unix() {
|
|
c.Data["json"] = map[string]string{
|
|
"token" : "token in the secret is expired and must be regenerated",
|
|
}
|
|
c.ServeJSON()
|
|
}
|
|
|
|
c.Data["json"] = map[string]bool{"ok": true}
|
|
c.ServeJSON()
|
|
|
|
}
|
|
|
|
// @name Get Admiralty Kubeconfig
|
|
// @description Retrieve a kubeconfig from the host with the token to authenticate as the SA from the namespace identified with execution id
|
|
// @Param dc_id path string true "which dc to contact"
|
|
// @Param execution path string true "execution id of the workflow"
|
|
// @Success 200
|
|
// @router /kubeconfig/:dc_id/:execution [get]
|
|
func (c *AdmiraltyController) GetAdmiraltyKubeconfig() {
|
|
dc_id := c.Ctx.Input.Param(":dc_id")
|
|
execution := c.Ctx.Input.Param(":execution")
|
|
_ = dc_id
|
|
|
|
serv, err := infrastructure.NewService()
|
|
if err != nil {
|
|
// change code to 500
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
generatedToken, err := serv.GenerateToken(c.Ctx.Request.Context(),execution,3600)
|
|
if err != nil {
|
|
fmt.Println("Couldn't generate a token for ns-", execution)
|
|
fmt.Println(err)
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
kubeconfig, err := NewHostKubeWithToken(generatedToken)
|
|
if err != nil {
|
|
fmt.Println("Could not retrieve the Kubeconfig edited with token")
|
|
fmt.Println(err)
|
|
c.Ctx.Output.SetStatus(500)
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = kubeconfig
|
|
c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
|
|
func NewHostKubeWithToken(token string) (*models.KubeConfigValue, error){
|
|
if len(token) == 0 {
|
|
return nil, fmt.Errorf("You didn't provide a token to be inserted in the Kubeconfig")
|
|
}
|
|
|
|
encodedCA := base64.StdEncoding.EncodeToString([]byte(conf.GetConfig().KubeCA))
|
|
|
|
hostKube := models.KubeConfigValue{
|
|
APIVersion: "v1",
|
|
CurrentContext: "default",
|
|
Kind: "Config",
|
|
Preferences: struct{}{},
|
|
Clusters: []models.KubeconfigNamedCluster{
|
|
{
|
|
Name: "default",
|
|
Cluster: models.KubeconfigCluster{
|
|
Server: conf.GetConfig().KubeHost,
|
|
CertificateAuthorityData: encodedCA,
|
|
},
|
|
},
|
|
},
|
|
Contexts: []models.KubeconfigNamedContext{
|
|
{
|
|
Name: "default",
|
|
Context: models.KubeconfigContext{
|
|
Cluster: "default",
|
|
User: "default",
|
|
},
|
|
},
|
|
},
|
|
Users: []models.KubeconfigUser{
|
|
models.KubeconfigUser{
|
|
Name: "default",
|
|
User: models.KubeconfigUserKeyPair{
|
|
Token: token,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return &hostKube, nil
|
|
} |