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  }