github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/postgres/commands.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package postgres 21 22 import ( 23 "fmt" 24 "strconv" 25 "strings" 26 27 corev1 "k8s.io/api/core/v1" 28 29 "github.com/1aal/kubeblocks/pkg/lorry/engines" 30 "github.com/1aal/kubeblocks/pkg/lorry/engines/models" 31 ) 32 33 var _ engines.ClusterCommands = &Commands{} 34 35 type Commands struct { 36 info engines.EngineInfo 37 examples map[models.ClientType]engines.BuildConnectExample 38 } 39 40 func NewCommands() engines.ClusterCommands { 41 return &Commands{ 42 info: engines.EngineInfo{ 43 Client: "psql", 44 Container: "postgresql", 45 PasswordEnv: "$PGPASSWORD", 46 UserEnv: "$PGUSER", 47 Database: "postgres", 48 }, 49 examples: map[models.ClientType]engines.BuildConnectExample{ 50 models.CLI: func(info *engines.ConnectionInfo) string { 51 return fmt.Sprintf(`# psql client connection example 52 psql -h%s -p %s -U %s %s 53 `, info.Host, info.Port, info.User, info.Database) 54 }, 55 56 models.DJANGO: func(info *engines.ConnectionInfo) string { 57 return fmt.Sprintf(`# .env 58 DB_HOST=%s 59 DB_NAME=%s 60 DB_USER=%s 61 DB_PASSWORD=%s 62 DB_PORT=%s 63 64 # settings.py 65 DATABASES = { 66 'default': { 67 'ENGINE': 'django.db.backends.postgresql', 68 'NAME': os.environ.get('DB_NAME'), 69 'HOST': os.environ.get('DB_HOST'), 70 'PORT': os.environ.get('DB_PORT'), 71 'USER': os.environ.get('DB_USER'), 72 'PASSWORD': os.environ.get('DB_PASSWORD'), 73 } 74 } 75 `, info.Host, info.Database, info.User, info.Password, info.Port) 76 }, 77 78 models.DOTNET: func(info *engines.ConnectionInfo) string { 79 return fmt.Sprintf(`# Startup.cs 80 var connectionString = "Host=%s;Port=%s;Username=%s;Password=%s;Database=%s"; 81 await using var dataSource = NpgsqlDataSource.Create(connectionString); 82 `, info.Host, info.Port, info.User, info.Password, info.Database) 83 }, 84 85 models.GO: func(info *engines.ConnectionInfo) string { 86 const goConnectExample = `# main.go 87 package main 88 89 import ( 90 "database/sql" 91 "log" 92 "os" 93 94 _ "github.com/lib/pq" 95 ) 96 97 func main() { 98 db, err := sql.Open("postgres", os.Getenv("DSN")) 99 if err != nil { 100 log.Fatalf("failed to connect: %v", err) 101 } 102 defer db.Close() 103 104 if err := db.Ping(); err != nil { 105 log.Fatalf("failed to ping: %v", err) 106 } 107 108 log.Println("Successfully connected!") 109 } 110 ` 111 dsn := fmt.Sprintf(`# .env 112 DSN=%s:%s@tcp(%s:%s)/%s 113 `, info.User, info.Password, info.Host, info.Port, info.Database) 114 return fmt.Sprintf("%s\n%s", dsn, goConnectExample) 115 }, 116 117 models.JAVA: func(info *engines.ConnectionInfo) string { 118 return fmt.Sprintf(`Class.forName("org.postgresql.Driver"); 119 Connection conn = DriverManager.getConnection( 120 "jdbc:postgresql://%s:%s/%s?user=%s&password=%s"); 121 `, info.Host, info.Port, info.Database, info.User, info.Password) 122 }, 123 124 models.NODEJS: func(info *engines.ConnectionInfo) string { 125 return fmt.Sprintf(`# .env 126 DATABASE_URL='postgres://%s:%s@%s:%s/%s' 127 128 # app.js 129 require('dotenv').config(); 130 const postgres = require('postgres'); 131 const connection = postgres(process.env.DATABASE_URL); 132 connection.end(); 133 `, info.User, info.Password, info.Host, info.Port, info.Database) 134 }, 135 136 models.PHP: func(info *engines.ConnectionInfo) string { 137 return fmt.Sprintf(`# .env 138 HOST=%s 139 PORT=%s 140 USERNAME=%s 141 PASSWORD=%s 142 DATABASE=%s 143 144 # index.php 145 <?php 146 $dbconn =pg_connect($_ENV["HOST"], $_ENV["USERNAME"], $_ENV["PASSWORD"], $_ENV["DATABASE"], $_ENV["PORT"]); 147 pg_close($dbconn) 148 ?> 149 `, info.Host, info.Port, info.User, info.Password, info.Database) 150 }, 151 152 models.PRISMA: func(info *engines.ConnectionInfo) string { 153 return fmt.Sprintf(`# .env 154 DATABASE_URL='postgres://%s:%s@%s:%s/%s' 155 156 # schema.prisma 157 generator client { 158 provider = "prisma-client-js" 159 } 160 161 datasource db { 162 provider = "postgresql" 163 url = env("DATABASE_URL") 164 relationMode = "prisma" 165 } 166 `, info.User, info.Password, info.Host, info.Port, info.Database) 167 }, 168 169 models.PYTHON: func(info *engines.ConnectionInfo) string { 170 return fmt.Sprintf(`# run the following command in the terminal to install dependencies 171 pip install python-dotenv psycopg2 172 173 # .env 174 HOST=%s 175 PORT=%s 176 USERNAME=%s 177 PASSWORD=%s 178 DATABASE=%s 179 180 # main.py 181 from dotenv import load_dotenv 182 load_dotenv() 183 import os 184 import MySQLdb 185 186 connection = psycopg2.connect( 187 host= os.getenv("HOST"), 188 port=os.getenv("PORT"), 189 user=os.getenv("USERNAME"), 190 password=os.getenv("PASSWORD"), 191 database=os.getenv("DATABASE"), 192 ) 193 `, info.Host, info.Port, info.User, info.Password, info.Database) 194 }, 195 196 models.RAILS: func(info *engines.ConnectionInfo) string { 197 return fmt.Sprintf(`# Gemfile 198 gem 'pg' 199 200 # config/database.yml 201 development: 202 <<: *default 203 adapter: postgresql 204 database: %s 205 username: %s 206 host: %s 207 password: %s 208 `, info.Database, info.User, info.Host, info.Password) 209 }, 210 211 models.RUST: func(info *engines.ConnectionInfo) string { 212 return fmt.Sprintf(`# run the following command in the terminal 213 export DATABASE_URL="postgresql://%s:%s@%s:%s/%s" 214 215 # src/main.rs 216 use std::env; 217 218 fn main() { 219 let url = env::var("DATABASE_URL").expect("DATABASE_URL not found"); 220 let conn = Connection::connect(url, TlsMode::None).unwrap(); 221 println!("Successfully connected!"); 222 } 223 224 # Cargo.toml 225 [package] 226 name = "kubeblocks_hello_world" 227 version = "0.0.1" 228 `, info.User, info.Password, info.Host, info.Port, info.Database) 229 }, 230 231 models.SYMFONY: func(info *engines.ConnectionInfo) string { 232 return fmt.Sprintf(`# .env 233 DATABASE_URL='postgresql://%s:%s@%s:%s/%s' 234 `, info.User, info.Password, info.Host, info.Port, info.Database) 235 }, 236 }, 237 } 238 } 239 240 func (m *Commands) ConnectCommand(connectInfo *engines.AuthInfo) []string { 241 userName := m.info.UserEnv 242 userPass := m.info.PasswordEnv 243 244 if connectInfo != nil { 245 userName = connectInfo.UserName 246 userPass = connectInfo.UserPasswd 247 } 248 249 // please refer to PostgreSQL documentation for more details 250 // https://www.postgresql.org/docs/current/libpq-envars.html 251 cmd := []string{fmt.Sprintf("PGUSER=%s PGPASSWORD=%s PGDATABASE=%s %s", userName, userPass, m.info.Database, m.info.Client)} 252 return []string{"sh", "-c", strings.Join(cmd, " ")} 253 } 254 255 func (m *Commands) Container() string { 256 return m.info.Container 257 } 258 259 func (m *Commands) ConnectExample(info *engines.ConnectionInfo, client string) string { 260 if len(info.Database) == 0 { 261 info.Database = m.info.Database 262 } 263 return engines.BuildExample(info, client, m.examples) 264 } 265 266 func (m *Commands) ExecuteCommand(scripts []string) ([]string, []corev1.EnvVar, error) { 267 cmd := []string{} 268 cmd = append(cmd, "/bin/sh", "-c", "-ex") 269 args := []string{} 270 for _, script := range scripts { 271 // split each script with a new line 272 lines := strings.Split(script, "\n") 273 for _, line := range lines { 274 args = append(args, fmt.Sprintf("-c %s", strconv.Quote(line))) 275 } 276 } 277 cmd = append(cmd, fmt.Sprintf("%s %s", m.info.Client, strings.Join(args, " "))) 278 envVars := []corev1.EnvVar{ 279 { 280 Name: "PGHOST", 281 Value: fmt.Sprintf("$(%s)", engines.EnvVarMap[engines.HOST]), 282 }, 283 { 284 Name: "PGUSER", 285 Value: fmt.Sprintf("$(%s)", engines.EnvVarMap[engines.USER]), 286 }, 287 { 288 Name: "PGPASSWORD", 289 Value: fmt.Sprintf("$(%s)", engines.EnvVarMap[engines.PASSWORD]), 290 }, 291 { 292 Name: "PGDATABASE", 293 Value: m.info.Database, 294 }, 295 } 296 return cmd, envVars, nil 297 }