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 }