2025-02-14 11:09:31 +01:00
|
|
|
package infrastructure
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2025-02-20 12:46:24 +01:00
|
|
|
"encoding/json"
|
2025-02-14 11:09:31 +01:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"oc-datacenter/conf"
|
|
|
|
|
|
|
|
authv1 "k8s.io/api/authentication/v1"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
"k8s.io/client-go/rest"
|
|
|
|
)
|
|
|
|
|
|
|
|
type KubernetesService struct {
|
|
|
|
Set *kubernetes.Clientset
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewKubernetesService() (Infrastructure, 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),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Create clientset
|
|
|
|
clientset, err := kubernetes.NewForConfig(config)
|
|
|
|
fmt.Println("NewForConfig", clientset, err)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.New("Error creating Kubernetes client: " + err.Error())
|
|
|
|
}
|
|
|
|
if clientset == nil {
|
|
|
|
return nil, errors.New("Error creating Kubernetes client: clientset is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &KubernetesService{
|
|
|
|
Set: clientset,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KubernetesService) CreateNamespace(ctx context.Context, ns string) error {
|
|
|
|
// Define the namespace
|
|
|
|
namespace := &v1.Namespace{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: ns,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Create the namespace
|
|
|
|
fmt.Println("Creating namespace...", k.Set)
|
|
|
|
if _, err := k.Set.CoreV1().Namespaces().Create(ctx, namespace, metav1.CreateOptions{}); err != nil {
|
|
|
|
return errors.New("Error creating namespace: " + err.Error())
|
|
|
|
}
|
|
|
|
fmt.Println("Namespace created successfully!")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KubernetesService) CreateServiceAccount(ctx context.Context, ns string) error {
|
|
|
|
// Create the ServiceAccount object
|
|
|
|
serviceAccount := &v1.ServiceAccount{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "sa-" + ns,
|
|
|
|
Namespace: ns,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Create the ServiceAccount in the specified namespace
|
|
|
|
_, err := k.Set.CoreV1().ServiceAccounts(ns).Create(ctx, serviceAccount, metav1.CreateOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return errors.New("Failed to create ServiceAccount: " + err.Error())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KubernetesService) CreateRole(ctx context.Context, ns string, role string, groups [][]string, resources [][]string, verbs [][]string) error {
|
|
|
|
// Create the Role object
|
|
|
|
if len(groups) != len(resources) || len(resources) != len(verbs) {
|
|
|
|
return errors.New("Invalid input: groups, resources, and verbs must have the same length")
|
|
|
|
}
|
|
|
|
rules := []rbacv1.PolicyRule{}
|
|
|
|
for i, group := range groups {
|
|
|
|
rules = append(rules, rbacv1.PolicyRule{
|
|
|
|
APIGroups: group,
|
|
|
|
Resources: resources[i],
|
|
|
|
Verbs: verbs[i],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
r := &rbacv1.Role{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: role,
|
|
|
|
Namespace: ns,
|
|
|
|
},
|
|
|
|
Rules: rules,
|
|
|
|
}
|
|
|
|
// Create the Role in the specified namespace
|
|
|
|
_, err := k.Set.RbacV1().Roles(ns).Create(ctx, r, metav1.CreateOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return errors.New("Failed to create Role: " + err.Error())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KubernetesService) CreateRoleBinding(ctx context.Context, ns string, roleBinding string, role string) error {
|
|
|
|
// Create the RoleBinding object
|
|
|
|
rb := &rbacv1.RoleBinding{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: roleBinding,
|
|
|
|
Namespace: ns,
|
|
|
|
},
|
|
|
|
Subjects: []rbacv1.Subject{
|
|
|
|
{
|
|
|
|
Kind: "ServiceAccount",
|
|
|
|
Name: "sa-" + ns,
|
|
|
|
Namespace: ns,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RoleRef: rbacv1.RoleRef{
|
|
|
|
Kind: "Role",
|
|
|
|
Name: role,
|
|
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Create the RoleBinding in the specified namespace
|
|
|
|
_, err := k.Set.RbacV1().RoleBindings(ns).Create(ctx, rb, metav1.CreateOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return errors.New("Failed to create RoleBinding: " + err.Error())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KubernetesService) DeleteNamespace(ctx context.Context, ns string) error {
|
|
|
|
// Delete the namespace
|
|
|
|
if err := k.Set.CoreV1().Namespaces().Delete(ctx, ns, metav1.DeleteOptions{}); err != nil {
|
|
|
|
return errors.New("Error deleting namespace: " + err.Error())
|
|
|
|
}
|
|
|
|
fmt.Println("Namespace deleted successfully!")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KubernetesService) GetToken(ctx context.Context, ns string, duration int) (string, error) {
|
|
|
|
// Define TokenRequest (valid for 1 hour)
|
|
|
|
d := int64(duration)
|
|
|
|
tokenRequest := &authv1.TokenRequest{
|
|
|
|
Spec: authv1.TokenRequestSpec{
|
|
|
|
ExpirationSeconds: &d, // 1 hour validity
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Generate the token
|
|
|
|
token, err := k.Set.CoreV1().
|
|
|
|
ServiceAccounts(ns).
|
|
|
|
CreateToken(ctx, "sa-"+ns, tokenRequest, metav1.CreateOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.New("Failed to create token for ServiceAccount: " + err.Error())
|
|
|
|
}
|
|
|
|
return token.Status.Token, nil
|
|
|
|
}
|
2025-02-20 12:46:24 +01:00
|
|
|
|
|
|
|
func (k *KubernetesService) GetTargets(ctx context.Context) ([]string,error){
|
|
|
|
|
|
|
|
var listTargets []string
|
|
|
|
resp, err := k.Set.RESTClient().
|
|
|
|
Get().
|
|
|
|
AbsPath("/apis/multicluster.admiralty.io/v1alpha1/targets").
|
|
|
|
DoRaw(ctx) // from https://stackoverflow.com/questions/60764908/how-to-access-kubernetes-crd-using-client-go
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("TODO : handle the error generated when contacting kube API")
|
|
|
|
fmt.Println("Error from k8s API : ", err)
|
|
|
|
return nil,err
|
|
|
|
}
|
|
|
|
fmt.Println(string(resp))
|
|
|
|
var targetDict map[string]interface{}
|
|
|
|
err = json.Unmarshal(resp,&targetDict)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("TODO: handle the error when unmarshalling k8s API response")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, _ := json.MarshalIndent(targetDict,""," ")
|
|
|
|
fmt.Println(string(b))
|
|
|
|
|
|
|
|
data := targetDict["items"].([]interface{})
|
|
|
|
|
|
|
|
for _, item := range data {
|
|
|
|
var metadata metav1.ObjectMeta
|
|
|
|
item := item.(map[string]interface{})
|
|
|
|
byteMetada, err := json.Marshal(item["metadata"])
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error while Marshalling metadata field")
|
|
|
|
return nil,err
|
|
|
|
}
|
|
|
|
err = json.Unmarshal(byteMetada,&metadata)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error while Unmarshalling metadata field to the library object")
|
|
|
|
return nil,err
|
|
|
|
}
|
|
|
|
|
|
|
|
listTargets = append(listTargets, metadata.Name)
|
|
|
|
}
|
|
|
|
// parse targets to retrieve the info we need
|
|
|
|
// fmt.Println(targetDict)
|
|
|
|
return listTargets,nil
|
|
|
|
|
|
|
|
}
|