github.com/metaprov/modela-operator@v0.0.0-20240118193048-f378be8b74d2/controllers/components/tenant.go (about)

     1  package components
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"github.com/metaprov/modela-operator/pkg/kube"
     7  	"github.com/metaprov/modela-operator/pkg/vault"
     8  	"golang.org/x/crypto/bcrypt"
     9  	k8serr "k8s.io/apimachinery/pkg/api/errors"
    10  	"sigs.k8s.io/controller-runtime/pkg/log"
    11  	"sigs.k8s.io/kustomize/kyaml/kio"
    12  
    13  	managementv1 "github.com/metaprov/modela-operator/api/v1alpha1"
    14  )
    15  
    16  type Tenant struct {
    17  	Name         string
    18  	ManifestPath string
    19  }
    20  
    21  func NewTenant(name string) *Tenant {
    22  	return &Tenant{
    23  		Name:         name,
    24  		ManifestPath: "tenant",
    25  	}
    26  }
    27  
    28  func (t Tenant) GetInstallPhase() managementv1.ModelaPhase {
    29  	return managementv1.ModelaPhaseInstallingTenant
    30  }
    31  
    32  func (t Tenant) IsEnabled(_ managementv1.Modela) bool {
    33  	return true
    34  }
    35  
    36  func (t Tenant) Installed(ctx context.Context) (bool, error) {
    37  	return kube.IsNamespaceCreated(t.Name)
    38  }
    39  
    40  func (t Tenant) Install(ctx context.Context, modela *managementv1.Modela, tenant *managementv1.TenantSpec) error {
    41  	logger := log.FromContext(ctx)
    42  
    43  	if err := kube.CreateNamespace(t.Name, modela.Name); err != nil && !k8serr.IsAlreadyExists(err) {
    44  		logger.Error(err, "failed to create namespace")
    45  		return err
    46  	}
    47  
    48  	var adminPassword string
    49  	if tenant.AdminPassword != nil {
    50  		adminPassword = *tenant.AdminPassword
    51  	} else {
    52  		adminPassword = "default"
    53  	}
    54  
    55  	hash, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.MinCost)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	if err := vault.ApplySecret(modela, fmt.Sprintf("tenant/%s/accounts/admin", t.Name), map[string]interface{}{
    61  		"password": string(hash),
    62  	}); err != nil {
    63  		return err
    64  	}
    65  
    66  	yaml, n, err := kube.LoadResources(t.ManifestPath, []kio.Filter{
    67  		kube.LabelFilter{Labels: map[string]string{"management.modela.ai/operator": modela.Name}},
    68  		kube.NamespaceFilter{Namespace: t.Name},
    69  		kube.TenantFilter{TenantName: t.Name},
    70  		kube.ConnectionFilter{
    71  			PgvectorEnabled: modela.Spec.Database.InstallPgvector,
    72  			MongoEnabled:    modela.Spec.Database.InstallMongoDB,
    73  		},
    74  	}, false)
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	if n > 0 {
    80  		logger.Info("Applying tenant resources", "tenant", t.Name, "length", len(yaml))
    81  		if err := kube.ApplyYaml(string(yaml)); err != nil {
    82  			return err
    83  		}
    84  	}
    85  
    86  	if values, err := kube.GetSecretValuesAsString("modela-system", "modela-storage-minio"); err == nil {
    87  		accessKey, _ := values["root-user"]
    88  		secretKey, _ := values["root-password"]
    89  
    90  		logger.Info("Applying minio secret")
    91  		if err := vault.ApplySecret(modela, fmt.Sprintf("tenant/%s/connections/minio-connection", t.Name), map[string]interface{}{
    92  			"accessKey": accessKey,
    93  			"secretKey": secretKey,
    94  			"host":      "modela-storage-minio.modela-system.svc.cluster.local:9000",
    95  		}); err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	if values, err := kube.GetSecretValuesAsString("modela-system", "modela-postgresql"); err == nil {
   101  		password, _ := values["postgres-password"]
   102  
   103  		logger.Info("Applying postgres secret")
   104  
   105  		for _, conn := range []string{"postgres-connection", "postgres-vector-connection"} {
   106  			if err := vault.ApplySecret(modela, fmt.Sprintf("tenant/%s/connections/%s", t.Name, conn), map[string]interface{}{
   107  				"username": "postgres",
   108  				"password": password,
   109  				"host":     "modela-postgresql.modela-system.svc.cluster.local",
   110  				"port":     "5432",
   111  			}); err != nil {
   112  				return err
   113  			}
   114  		}
   115  	}
   116  
   117  	if values, err := kube.GetSecretValuesAsString("modela-system", "modela-mongodb"); err == nil {
   118  		password, _ := values["mongodb-root-password"]
   119  
   120  		logger.Info("Applying mongo secret")
   121  		if err := vault.ApplySecret(modela, fmt.Sprintf("tenant/%s/connections/mongodb-connection", t.Name), map[string]interface{}{
   122  			"username": "root",
   123  			"password": password,
   124  			"host":     "modela-mongodb.modela-system.svc.cluster.local",
   125  			"port":     "27017",
   126  		}); err != nil {
   127  			return err
   128  		}
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  func (t Tenant) Installing(ctx context.Context) (bool, error) {
   135  	installed, err := t.Installed(ctx)
   136  	if !installed {
   137  		return installed, err
   138  	}
   139  	return false, nil
   140  }
   141  
   142  func (t Tenant) Ready(ctx context.Context) (bool, error) {
   143  	if _, missing, err := kube.LoadResources(t.ManifestPath, []kio.Filter{kube.NamespaceFilter{Namespace: t.Name}, kube.TenantFilter{TenantName: t.Name}}, false); missing > 0 {
   144  		return false, managementv1.ComponentMissingResourcesError
   145  	} else if err != nil {
   146  		return false, err
   147  	}
   148  	return true, nil
   149  }
   150  
   151  func (d Tenant) Uninstall(ctx context.Context, modela *managementv1.Modela) error {
   152  	if created, err := kube.IsNamespaceCreatedByOperator(d.Name, modela.Name); !created {
   153  		return managementv1.ComponentNotInstalledByModelaError
   154  	} else if err != nil {
   155  		return err
   156  	}
   157  
   158  	if err := kube.DeleteNamespace(d.Name); err != nil {
   159  		return err
   160  	}
   161  
   162  	return nil
   163  }