Uniformisation and verification of admiralty link with nodes + token
This commit is contained in:
parent
44abc073c4
commit
74ac2b6d9c
@ -5,8 +5,12 @@ import (
|
||||
"fmt"
|
||||
"oc-datacenter/infrastructure"
|
||||
"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 {
|
||||
@ -20,6 +24,12 @@ type Kubeconfig struct {
|
||||
Data *string
|
||||
}
|
||||
|
||||
type KubeUser struct {
|
||||
Name string
|
||||
User struct {
|
||||
Token string
|
||||
}
|
||||
}
|
||||
// Operations about the admiralty objects of the datacenter
|
||||
type AdmiraltyController struct {
|
||||
beego.Controller
|
||||
@ -76,9 +86,9 @@ func (c *AdmiraltyController) GetOneTarget() {
|
||||
// @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 serviceAccount body controllers.KubeInfo true "url and serviceAccount to use with the source formatted as json object"
|
||||
// @Param kubeconfigInfo body controllers.KubeInfo true "url and serviceAccount to use with the source formatted as json object"
|
||||
// @Success 200
|
||||
// @router /sources/:dc_id/:execution [post]
|
||||
// @router /source/:dc_id/:execution [post]
|
||||
func (c *AdmiraltyController) CreateSource() {
|
||||
var data KubeInfo
|
||||
json.Unmarshal(c.Ctx.Input.CopyBody(10000000),&data)
|
||||
@ -114,7 +124,7 @@ func (c *AdmiraltyController) CreateSource() {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := serv.CreateAdmiraltySource(execution)
|
||||
res, err := serv.CreateAdmiraltySource(c.Ctx.Request.Context(),execution)
|
||||
if err != nil {
|
||||
// change code to 500
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
@ -152,7 +162,7 @@ func (c *AdmiraltyController) CreateAdmiraltyTarget(){
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := serv.CreateAdmiraltyTarget(execution)
|
||||
resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(),execution)
|
||||
if err != nil {
|
||||
// change code to 500
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
@ -204,7 +214,7 @@ func(c *AdmiraltyController) GetKubeSecret() {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := serv.GetKubeconfigSecret(execution)
|
||||
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution)
|
||||
if err != nil {
|
||||
// change code to 500
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
@ -268,7 +278,7 @@ func (c *AdmiraltyController) CreateKubeSecret() {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := serv.CreateKubeconfigSecret(*kubeconfig.Data,execution)
|
||||
resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(),*kubeconfig.Data,execution)
|
||||
if err != nil {
|
||||
// change code to 500
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
@ -281,4 +291,101 @@ func (c *AdmiraltyController) CreateKubeSecret() {
|
||||
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()
|
||||
|
||||
}
|
@ -21,6 +21,8 @@ type BookingController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
var BookingExample booking.Booking
|
||||
|
||||
// @Title Search
|
||||
// @Description search bookings by execution
|
||||
// @Param id path string true "id execution"
|
||||
@ -209,7 +211,13 @@ func (o *BookingController) Post() {
|
||||
*/
|
||||
var resp booking.Booking
|
||||
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
||||
json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &resp)
|
||||
err := json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &resp)
|
||||
if err != nil {
|
||||
fmt.Println("Error unmarshalling")
|
||||
fmt.Println(err)
|
||||
fmt.Println(resp)
|
||||
}
|
||||
|
||||
dc_id := resp.ResourceID
|
||||
// delete all previous bookings
|
||||
isDraft := o.Ctx.Input.Query("is_draft")
|
||||
|
@ -40,7 +40,7 @@ func (o *SessionController) GetToken() {
|
||||
return
|
||||
}
|
||||
fmt.Println("BLAPO", id, duration)
|
||||
token, err := serv.GetToken(o.Ctx.Request.Context(), id, duration)
|
||||
token, err := serv.GenerateToken(o.Ctx.Request.Context(), id, duration)
|
||||
if err != nil {
|
||||
// change code to 500
|
||||
o.Ctx.Output.SetStatus(500)
|
||||
|
@ -4,20 +4,23 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"oc-datacenter/conf"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type Infrastructure interface {
|
||||
CreateNamespace(ctx context.Context, ns string) error
|
||||
DeleteNamespace(ctx context.Context, ns string) error
|
||||
GetToken(ctx context.Context, ns string, duration int) (string, error)
|
||||
GenerateToken(ctx context.Context, ns string, duration int) (string, error)
|
||||
CreateServiceAccount(ctx context.Context, ns 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
|
||||
GetTargets(ctx context.Context) ([]string,error)
|
||||
CreateAdmiraltySource(executionId string) ([]byte, error)
|
||||
CreateKubeconfigSecret(kubeconfig string, executionId string) ([]byte, error)
|
||||
GetKubeconfigSecret(executionId string) ([]byte, error)
|
||||
CreateAdmiraltyTarget(executionId string)([]byte,error)
|
||||
CreateAdmiraltySource(context context.Context,executionId string) ([]byte, error)
|
||||
CreateKubeconfigSecret(context context.Context,kubeconfig string, executionId string) ([]byte, error)
|
||||
GetKubeconfigSecret(context context.Context,executionId string) ([]byte, error)
|
||||
CreateAdmiraltyTarget(context context.Context,executionId string)([]byte,error)
|
||||
GetOneNode(context context.Context,executionID string) (*v1.Node, error)
|
||||
}
|
||||
|
||||
var _service = map[string]func() (Infrastructure, error){
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"oc-datacenter/conf"
|
||||
"strings"
|
||||
|
||||
authv1 "k8s.io/api/authentication/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@ -77,6 +78,7 @@ func NewRemoteKubernetesService(url string, ca string, cert string, key string)
|
||||
|
||||
func (k *KubernetesService) CreateNamespace(ctx context.Context, ns string) error {
|
||||
// Define the namespace
|
||||
fmt.Println("ExecutionID in CreateNamespace() : ", ns)
|
||||
namespace := &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ns,
|
||||
@ -172,7 +174,7 @@ func (k *KubernetesService) DeleteNamespace(ctx context.Context, ns string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KubernetesService) GetToken(ctx context.Context, ns string, duration int) (string, error) {
|
||||
func (k *KubernetesService) GenerateToken(ctx context.Context, ns string, duration int) (string, error) {
|
||||
// Define TokenRequest (valid for 1 hour)
|
||||
d := int64(duration)
|
||||
tokenRequest := &authv1.TokenRequest{
|
||||
@ -190,6 +192,8 @@ func (k *KubernetesService) GetToken(ctx context.Context, ns string, duration in
|
||||
return token.Status.Token, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Needs refactoring :
|
||||
// - 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){
|
||||
@ -244,15 +248,15 @@ 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 delcared a serviceAccount with sufficient permission to create pods
|
||||
func (k *KubernetesService) CreateAdmiraltyTarget(executionId string)([]byte,error){
|
||||
exists, err := k.GetKubeconfigSecret(executionId)
|
||||
func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context,executionId string)([]byte,error){
|
||||
exists, err := k.GetKubeconfigSecret(context,executionId)
|
||||
if err != nil {
|
||||
fmt.Println("Error verifying kube-secret before creating target")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists == nil {
|
||||
fmt.Println("Target needs to be binded to a secret in ns-",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
|
||||
}
|
||||
|
||||
@ -269,8 +273,8 @@ func (k *KubernetesService) CreateAdmiraltyTarget(executionId string)([]byte,err
|
||||
targetManifest = tpl.String()
|
||||
resp, err := postCDRapiKube(
|
||||
*k.Set,
|
||||
context.TODO(),
|
||||
"/apis/multicluster.admiralty.io/v1alpha1/namespaces/ns-"+ executionId +"/targets",
|
||||
context,
|
||||
"/apis/multicluster.admiralty.io/v1alpha1/namespaces/"+ executionId +"/targets",
|
||||
[]byte(targetManifest),
|
||||
map[string]string{"fieldManager":"kubectl-client-side-apply"},
|
||||
map[string]string{"fieldValidation":"Strict"},
|
||||
@ -293,7 +297,7 @@ func (k *KubernetesService) CreateAdmiraltyTarget(executionId string)([]byte,err
|
||||
// This method is temporary to implement the use of Admiralty, but must be edited
|
||||
// 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(executionId string) ([]byte, error) {
|
||||
func (k *KubernetesService) CreateAdmiraltySource(context context.Context,executionId string) ([]byte, error) {
|
||||
var sourceManifest string
|
||||
var tpl bytes.Buffer
|
||||
tmpl, err := template.New("source").
|
||||
@ -308,8 +312,8 @@ func (k *KubernetesService) CreateAdmiraltySource(executionId string) ([]byte, e
|
||||
|
||||
resp, err := postCDRapiKube(
|
||||
*k.Set,
|
||||
context.TODO(),
|
||||
"/apis/multicluster.admiralty.io/v1alpha1/namespaces/ns-"+ executionId +"/sources",
|
||||
context,
|
||||
"/apis/multicluster.admiralty.io/v1alpha1/namespaces/"+ executionId +"/sources",
|
||||
[]byte(sourceManifest),
|
||||
map[string]string{"fieldManager":"kubectl-client-side-apply"},
|
||||
map[string]string{"fieldValidation":"Strict"},
|
||||
@ -326,7 +330,7 @@ func (k *KubernetesService) CreateAdmiraltySource(executionId string) ([]byte, e
|
||||
|
||||
// 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
|
||||
func (k *KubernetesService) CreateKubeconfigSecret(kubeconfig string, executionId string) ([]byte, error) {
|
||||
func (k *KubernetesService) CreateKubeconfigSecret(context context.Context,kubeconfig string, executionId string) ([]byte, error) {
|
||||
config, err := base64.StdEncoding.DecodeString(kubeconfig)
|
||||
// config, err := base64.RawStdEncoding.DecodeString(kubeconfig)
|
||||
if err != nil {
|
||||
@ -338,20 +342,20 @@ func (k *KubernetesService) CreateKubeconfigSecret(kubeconfig string, executionI
|
||||
secretManifest := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kube-secret-" + executionId,
|
||||
Namespace: "ns-" + executionId,
|
||||
Namespace: executionId,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config": config,
|
||||
},
|
||||
}
|
||||
|
||||
exists, err := k.GetKubeconfigSecret(executionId)
|
||||
exists, err := k.GetKubeconfigSecret(context,executionId)
|
||||
if err != nil {
|
||||
fmt.Println("Error verifying if kube secret exists in ns-", executionId)
|
||||
fmt.Println("Error verifying if kube secret exists in namespace ", executionId)
|
||||
return nil, err
|
||||
}
|
||||
if exists != nil {
|
||||
fmt.Println("kube-secret already exists in ns-", executionId)
|
||||
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)
|
||||
@ -359,8 +363,8 @@ func (k *KubernetesService) CreateKubeconfigSecret(kubeconfig string, executionI
|
||||
_ = err
|
||||
}
|
||||
resp, err := k.Set.CoreV1().
|
||||
Secrets("ns-"+executionId).
|
||||
Create(context.TODO(),secretManifest,metav1.CreateOptions{})
|
||||
Secrets(executionId).
|
||||
Create(context,secretManifest,metav1.CreateOptions{})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error while trying to contact API to get secret kube-secret-"+executionId)
|
||||
@ -377,10 +381,10 @@ func (k *KubernetesService) CreateKubeconfigSecret(kubeconfig string, executionI
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesService) GetKubeconfigSecret(executionId string) ([]byte, error) {
|
||||
func (k *KubernetesService) GetKubeconfigSecret(context context.Context,executionId string) ([]byte, error) {
|
||||
resp, err := k.Set.CoreV1().
|
||||
Secrets("ns-"+executionId).
|
||||
Get(context.TODO(),"kube-secret-"+executionId,metav1.GetOptions{})
|
||||
Secrets(executionId).
|
||||
Get(context,"kube-secret-"+executionId,metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if(apierrors.IsNotFound(err)){
|
||||
@ -441,4 +445,26 @@ func postCDRapiKube(client kubernetes.Clientset, ctx context.Context, path strin
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesService) GetOneNode(context context.Context,executionID string) (*v1.Node, error) {
|
||||
res, err := k.Set.CoreV1().
|
||||
Nodes().
|
||||
List(
|
||||
context,
|
||||
metav1.ListOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println("Error getting the list of nodes from k8s API")
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, node := range res.Items {
|
||||
if isNode := strings.Contains(node.Name,"admiralty-"+executionID+"-target-"+executionID+"-"); isNode {
|
||||
return &node, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user