package controllers import ( "context" "errors" "fmt" "log" "github.com/joho/godotenv" "git.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/kube" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" ) type Environemnt struct { Controller ctrl.Manager UserID string Data *EnvironemntData Token string } type EnvironemntData struct { Name string Description string Provider string Kubernetes string Location string ServerType string } func (e *EnvironemntData) buildVars() (string, error) { vars := fmt.Sprintf(`# -- Generated by the softplayer controller SP_PROVIDER=%s SP_KUBERNETES=%s SP_SERVER_TYPE=%s SP_SERVER_LOCATION=%s`, e.Provider, e.Kubernetes, e.ServerType, e.Location, ) return vars, nil } func (env *Environemnt) isNsVerified(ctx context.Context) error { client := env.Controller.GetClient() ns := &corev1.Namespace{} if err := client.Get(ctx, types.NamespacedName{Name: env.UserID}, ns); err != nil { return err } val, ok := ns.GetLabels()["email-verified"] if !ok || val == "false" { return errors.New("user email is not verified, can't create an new env") } return nil } // Create environment should create a new configmap in the user's namespace // using a token that belongs to the user. func (env *Environemnt) Create(ctx context.Context) error { if err := env.isNsVerified(ctx); err != nil { log.Println("Can't verify ns") return err } env.Controller.GetClient() conf := &rest.Config{ Host: "https://kubernetes.default.svc.cluster.local:443", BearerToken: env.Token, TLSClientConfig: rest.TLSClientConfig{ Insecure: true, }, } controller, err := ctrl.NewManager(conf, ctrl.Options{}) if err != nil { return err } vars, err := env.Data.buildVars() if err != nil { return err } obj := corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: env.Data.Name, Namespace: env.UserID, Labels: map[string]string{ "component": "bootstrap", "kind": "environment", }, }, Data: map[string]string{ "description": env.Data.Description, "vars": vars, }, } if err := kube.Create(ctx, controller.GetClient(), &obj, false); err != nil { return err } return nil } func (env *Environemnt) Delete(ctx context.Context) error { env.Controller.GetClient() conf := &rest.Config{ Host: "https://kubernetes.default.svc.cluster.local:443", BearerToken: env.Token, TLSClientConfig: rest.TLSClientConfig{ Insecure: true, }, } controller, err := ctrl.NewManager(conf, ctrl.Options{}) if err != nil { return err } obj := corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: env.Data.Name, Namespace: env.UserID, Labels: map[string]string{ "component": "bootstrap", }, }, } if err := kube.Delete(ctx, controller.GetClient(), &obj, false); err != nil { return err } return nil } func (env *Environemnt) ListEnvs(ctx context.Context) ([]string, error) { env.Controller.GetClient() conf := &rest.Config{ Host: "https://kubernetes.default.svc.cluster.local:443", BearerToken: env.Token, TLSClientConfig: rest.TLSClientConfig{ Insecure: true, }, } clientset, err := kubernetes.NewForConfig(conf) if err != nil { return []string{}, err } secrets, err := clientset.CoreV1().ConfigMaps(env.UserID).List(ctx, metav1.ListOptions{LabelSelector: "kind=environment"}) if err != nil { return []string{}, err } result := []string{} log.Println(secrets) for _, env := range secrets.Items { result = append(result, env.Name) } return result, nil } func (env *Environemnt) Get(ctx context.Context) error { env.Controller.GetClient() conf := &rest.Config{ Host: "https://kubernetes.default.svc.cluster.local:443", BearerToken: env.Token, TLSClientConfig: rest.TLSClientConfig{ Insecure: true, }, } clientset, err := kubernetes.NewForConfig(conf) if err != nil { return err } envData, err := clientset.CoreV1().ConfigMaps(env.UserID).Get(ctx, env.Data.Name, metav1.GetOptions{}) if err != nil { return err } res, err := godotenv.Unmarshal(envData.Data["vars"]) if err != nil { return err } // env.Data.Provider = res["SP_PROVIDER"] env.Data.Kubernetes = res["SP_KUBERNETES"] return nil }