Compare commits
No commits in common. "main" and "demo-alpr" have entirely different histories.
@ -30,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
|
||||||
|
@ -10,13 +10,10 @@ 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 {
|
||||||
@ -142,12 +139,6 @@ 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()}
|
||||||
@ -324,6 +315,7 @@ func (c *AdmiraltyController) CreateKubeSecret() {
|
|||||||
// @router /node/:execution [get]
|
// @router /node/:execution [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")
|
||||||
|
|
||||||
|
|
||||||
@ -347,14 +339,12 @@ 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{
|
||||||
"node" : "the node for " + execution + " can't be found, make sure both target and source resources are set up on local and remote hosts",
|
"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()
|
c.ServeJSON()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution)
|
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// change code to 500
|
// change code to 500
|
||||||
@ -369,6 +359,7 @@ 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{}
|
||||||
@ -402,15 +393,14 @@ func (c *AdmiraltyController) GetNodeReady(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *isExpired {
|
if *isExpired {
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]string{
|
||||||
"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]interface{}{"node": node,"token": true}
|
c.Data["json"] = map[string]bool{"ok": true}
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -436,8 +426,6 @@ 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")
|
||||||
@ -449,11 +437,7 @@ 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()
|
||||||
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
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"
|
||||||
|
|
||||||
@ -14,40 +16,12 @@ 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) {
|
||||||
@ -59,7 +33,6 @@ 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)
|
||||||
@ -70,7 +43,6 @@ 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
|
||||||
@ -110,9 +82,6 @@ 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
|
||||||
@ -291,29 +260,33 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context, execu
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
target := map[string]interface{}{
|
var targetManifest string
|
||||||
"apiVersion": "multicluster.admiralty.io/v1alpha1",
|
var tpl bytes.Buffer
|
||||||
"kind": "Target",
|
tmpl, err := template.New("target").
|
||||||
"metadata": map[string]interface{}{
|
Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Target\", \"metadata\": {\"name\": \"target-{{.ExecutionId}}\"}, \"spec\": { \"kubeconfigSecret\" :{\"name\": \"kube-secret-{{.ExecutionId}}\"}} }")
|
||||||
"name": "target-"+executionId,
|
|
||||||
"namespace": executionId,
|
|
||||||
},
|
|
||||||
"spec": map[string]interface{}{
|
|
||||||
"kubeconfigSecret": map[string]string{
|
|
||||||
"name" : "kube-secret-"+executionId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := dynamicClientApply(executionId, "target", gvrTargets, context, target)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Error when trying to apply Source definition :" + err.Error())
|
fmt.Println("Error creating the template for the target Manifest")
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
err = tmpl.Execute(&tpl, map[string]string{"ExecutionId": executionId})
|
||||||
|
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
|
||||||
@ -324,27 +297,35 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context, execu
|
|||||||
// This method is temporary to implement the use of Admiralty, but must be edited
|
// 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
|
// 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
|
||||||
source := map[string]interface{}{
|
var tpl bytes.Buffer
|
||||||
"apiVersion": "multicluster.admiralty.io/v1alpha1",
|
tmpl, err := template.New("source").
|
||||||
"kind": "Source",
|
Parse("{\"apiVersion\": \"multicluster.admiralty.io/v1alpha1\", \"kind\": \"Source\", \"metadata\": {\"name\": \"source-{{.ExecutionId}}\"}, \"spec\": {\"serviceAccountName\": \"sa-{{.ExecutionId}}\"} }")
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "source-"+executionId,
|
|
||||||
"namespace": executionId,
|
|
||||||
},
|
|
||||||
"spec": map[string]interface{}{
|
|
||||||
"serviceAccountName": "sa-"+executionId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
res, err := dynamicClientApply(executionId, "source",gvrSources, context, source)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Error when trying to apply Source definition :" + err.Error())
|
fmt.Println("Error creating the template for the source Manifest")
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
err = tmpl.Execute(&tpl, map[string]string{"ExecutionId": executionId})
|
||||||
|
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
|
||||||
@ -358,38 +339,33 @@ func (k *KubernetesService) CreateKubeconfigSecret(context context.Context, kube
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
secretApplyConfig := apply.Secret("kube-secret-" + executionId,
|
secretManifest := &v1.Secret{
|
||||||
executionId).
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
WithData(map[string][]byte{
|
Name: "kube-secret-" + executionId,
|
||||||
"config": config,
|
Namespace: executionId,
|
||||||
},
|
},
|
||||||
)
|
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).
|
||||||
Apply(context,
|
Create(context, secretManifest, metav1.CreateOptions{})
|
||||||
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)
|
||||||
@ -449,39 +425,7 @@ func getCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dynamicClientApply(executionId string, typeResource string, resourceDefinition schema.GroupVersionResource, ctx context.Context, object map[string]interface{}) ([]byte, error) {
|
func postCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string, body []byte, params ...map[string]string) ([]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,
|
|
||||||
typeResource + "-" + executionId,
|
|
||||||
&unstructured.Unstructured{Object: object},
|
|
||||||
metav1.ApplyOptions{
|
|
||||||
FieldManager: "kubectl-client-side-apply",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error from k8s API when applying " + fmt.Sprint(object) + " 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).
|
||||||
|
Loading…
Reference in New Issue
Block a user