github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/mysql/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 mysql 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: "mysql", 44 PasswordEnv: "$MYSQL_ROOT_PASSWORD", 45 UserEnv: "$MYSQL_ROOT_USER", 46 Database: "mysql", 47 }, 48 examples: map[models.ClientType]engines.BuildConnectExample{ 49 models.CLI: func(info *engines.ConnectionInfo) string { 50 return fmt.Sprintf(`# mysql client connection example 51 mysql -h %s -P %s -u %s -p%s 52 `, info.Host, info.Port, info.User, info.Password) 53 }, 54 55 models.DJANGO: func(info *engines.ConnectionInfo) string { 56 return fmt.Sprintf(`# .env 57 DB_HOST=%s 58 DB_NAME=%s 59 DB_USER=%s 60 DB_PASSWORD=%s 61 DB_PORT=%s 62 63 # settings.py 64 DATABASES = { 65 'default': { 66 'ENGINE': 'django.db.backends.mysql', 67 'NAME': os.environ.get('DB_NAME'), 68 'HOST': os.environ.get('DB_HOST'), 69 'PORT': os.environ.get('DB_PORT'), 70 'USER': os.environ.get('DB_USER'), 71 'PASSWORD': os.environ.get('DB_PASSWORD'), 72 } 73 } 74 `, info.Host, info.Database, info.User, info.Password, info.Port) 75 }, 76 77 models.DOTNET: func(info *engines.ConnectionInfo) string { 78 return fmt.Sprintf(`# appsettings.json 79 { 80 "ConnectionStrings": { 81 "Default": "server=%s;port=%s;database=%s;user=%s;password=%s;SslMode=VerifyFull;" 82 }, 83 } 84 85 # Startup.cs 86 public void ConfigureServices(IServiceCollection services) 87 { 88 services.AddTransient<MySqlConnection>(_ => new MySqlConnection(Configuration["ConnectionStrings:Default"])); 89 } 90 `, info.Host, info.Port, info.Database, info.User, info.Password) 91 }, 92 93 models.GO: func(info *engines.ConnectionInfo) string { 94 const goConnectExample = `# main.go 95 package main 96 97 import ( 98 "database/sql" 99 "log" 100 "os" 101 102 _ "github.com/go-sql-driver/mysql" 103 ) 104 105 func main() { 106 db, err := sql.Open("mysql", os.Getenv("DSN")) 107 if err != nil { 108 log.Fatalf("failed to connect: %v", err) 109 } 110 defer db.Close() 111 112 if err := db.Ping(); err != nil { 113 log.Fatalf("failed to ping: %v", err) 114 } 115 116 log.Println("Successfully connected!") 117 } 118 ` 119 dsn := fmt.Sprintf(`# .env 120 DSN=%s:%s@tcp(%s:%s)/%s?tls=true 121 `, info.User, info.Password, info.Host, info.Port, info.Database) 122 return fmt.Sprintf("%s\n%s", dsn, goConnectExample) 123 }, 124 125 models.JAVA: func(info *engines.ConnectionInfo) string { 126 return fmt.Sprintf(`Class.forName("com.mysql.cj.jdbc.Driver"); 127 Connection conn = DriverManager.getConnection( 128 "jdbc:mysql://%s:%s/%s?sslMode=VERIFY_IDENTITY", 129 "%s", 130 "%s"); 131 `, info.Host, info.Port, info.Database, info.User, info.Password) 132 }, 133 134 models.NODEJS: func(info *engines.ConnectionInfo) string { 135 return fmt.Sprintf(`# .env 136 DATABASE_URL='mysql://%s:%s@%s:%s/%s?ssl={"rejectUnauthorized":true}' 137 138 # app.js 139 require('dotenv').config(); 140 const mysql = require('mysql2'); 141 const connection = mysql.createConnection(process.env.DATABASE_URL); 142 connection.end(); 143 `, info.User, info.Password, info.Host, info.Port, info.Database) 144 }, 145 146 models.PHP: func(info *engines.ConnectionInfo) string { 147 return fmt.Sprintf(`# .env 148 HOST=%s 149 PORT=%s 150 USERNAME=%s 151 PASSWORD=%s 152 DATABASE=%s 153 154 # index.php 155 <?php 156 $mysqli = mysqli_init(); 157 $mysqli->real_connect($_ENV["HOST"], $_ENV["USERNAME"], $_ENV["PASSWORD"], $_ENV["DATABASE"], $_ENV["PORT"]); 158 $mysqli->close(); 159 ?> 160 `, info.Host, info.Port, info.User, info.Password, info.Database) 161 }, 162 163 models.PRISMA: func(info *engines.ConnectionInfo) string { 164 return fmt.Sprintf(`# .env 165 DATABASE_URL='mysql://%s:%s@%s:%s/%s?sslaccept=strict' 166 167 # schema.prisma 168 generator client { 169 provider = "prisma-client-js" 170 } 171 172 datasource db { 173 provider = "mysql" 174 url = env("DATABASE_URL") 175 relationMode = "prisma" 176 } 177 `, info.User, info.Password, info.Host, info.Port, info.Database) 178 }, 179 180 models.PYTHON: func(info *engines.ConnectionInfo) string { 181 return fmt.Sprintf(`# run the following command in the terminal to install dependencies 182 pip install python-dotenv mysqlclient 183 184 # .env 185 HOST=%s 186 PORT=%s 187 USERNAME=%s 188 PASSWORD=%s 189 DATABASE=%s 190 191 # main.py 192 from dotenv import load_dotenv 193 load_dotenv() 194 import os 195 import MySQLdb 196 197 connection = MySQLdb.connect( 198 host= os.getenv("HOST"), 199 port=os.getenv("PORT"), 200 user=os.getenv("USERNAME"), 201 passwd=os.getenv("PASSWORD"), 202 db=os.getenv("DATABASE"), 203 ssl_mode = "VERIFY_IDENTITY", 204 ) 205 `, info.Host, info.Port, info.User, info.Password, info.Database) 206 }, 207 208 models.RAILS: func(info *engines.ConnectionInfo) string { 209 return fmt.Sprintf(`# Gemfile 210 gem 'mysql2' 211 212 # config/database.yml 213 development: 214 <<: *default 215 adapter: mysql2 216 database: %s 217 username: %s 218 host: %s 219 password: %s 220 ssl_mode: verify_identity 221 `, info.Database, info.User, info.Host, info.Password) 222 }, 223 224 models.RUST: func(info *engines.ConnectionInfo) string { 225 return fmt.Sprintf(`# run the following command in the terminal 226 export DATABASE_URL="mysql://%s:%s@%s:%s/%s" 227 228 # src/main.rs 229 use std::env; 230 231 fn main() { 232 let url = env::var("DATABASE_URL").expect("DATABASE_URL not found"); 233 let builder = mysql::OptsBuilder::from_opts(mysql::Opts::from_url(&url).unwrap()); 234 let pool = mysql::Pool::new(builder.ssl_opts(mysql::SslOpts::default())).unwrap(); 235 let _conn = pool.get_conn().unwrap(); 236 println!("Successfully connected!"); 237 } 238 239 # Cargo.toml 240 [package] 241 name = "kubeblocks_hello_world" 242 version = "0.0.1" 243 244 [dependencies] 245 mysql = "*" 246 `, info.User, info.Password, info.Host, info.Port, info.Database) 247 }, 248 249 models.SYMFONY: func(info *engines.ConnectionInfo) string { 250 return fmt.Sprintf(`# .env 251 DATABASE_URL='mysql://%s:%s@%s:%s/%s' 252 `, info.User, info.Password, info.Host, info.Port, info.Database) 253 }, 254 }, 255 } 256 } 257 258 func (m *Commands) ConnectCommand(connectInfo *engines.AuthInfo) []string { 259 userName := m.info.UserEnv 260 userPass := m.info.PasswordEnv 261 262 if connectInfo != nil { 263 userName = connectInfo.UserName 264 userPass = connectInfo.UserPasswd 265 } 266 267 // avoid using env variables 268 // MYSQL_PWD is deprecated as of MySQL 8.0; expect it to be removed in a future version of MySQL. 269 // ref to mysql manual for more details. 270 // https://dev.mysql.com/doc/refman/8.0/en/environment-variables.html 271 mysqlCmd := []string{fmt.Sprintf("%s -u%s -p%s", m.info.Client, userName, userPass)} 272 273 return []string{"sh", "-c", strings.Join(mysqlCmd, " ")} 274 } 275 276 func (m *Commands) Container() string { 277 return m.info.Container 278 } 279 280 func (m *Commands) ConnectExample(info *engines.ConnectionInfo, client string) string { 281 if len(info.Database) == 0 { 282 info.Database = m.info.Database 283 } 284 return engines.BuildExample(info, client, m.examples) 285 } 286 287 func (m *Commands) ExecuteCommand(scripts []string) ([]string, []corev1.EnvVar, error) { 288 cmd := []string{} 289 cmd = append(cmd, "/bin/sh", "-c", "-ex") 290 cmd = append(cmd, fmt.Sprintf("%s -u%s -p%s -e %s", m.info.Client, 291 fmt.Sprintf("$%s", engines.EnvVarMap[engines.USER]), 292 fmt.Sprintf("$%s", engines.EnvVarMap[engines.PASSWORD]), 293 strconv.Quote(strings.Join(scripts, " ")))) 294 295 envs := []corev1.EnvVar{ 296 { 297 Name: "MYSQL_HOST", 298 Value: fmt.Sprintf("$(%s)", engines.EnvVarMap[engines.HOST]), 299 }, 300 } 301 return cmd, envs, nil 302 }