Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add new command gtctl connect mysql #86

Merged
merged 13 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/cmd/gtctl/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/spf13/cobra"

"github.com/GreptimeTeam/gtctl/pkg/cmd/gtctl/cluster/connect"
"github.com/GreptimeTeam/gtctl/pkg/cmd/gtctl/cluster/create"
"github.com/GreptimeTeam/gtctl/pkg/cmd/gtctl/cluster/delete"
"github.com/GreptimeTeam/gtctl/pkg/cmd/gtctl/cluster/get"
Expand Down Expand Up @@ -47,6 +48,7 @@ func NewClusterCommand(l logger.Logger) *cobra.Command {
cmd.AddCommand(scale.NewScaleClusterCommand(l))
cmd.AddCommand(get.NewGetClusterCommand(l))
cmd.AddCommand(list.NewListClustersCommand(l))
cmd.AddCommand(connect.NewConnectCommand(l))

return cmd
}
93 changes: 93 additions & 0 deletions pkg/cmd/gtctl/cluster/connect/connect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package connect

import (
"context"
"fmt"
"strings"

"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"

greptimedbclusterv1alpha1 "github.com/GreptimeTeam/greptimedb-operator/apis/v1alpha1"
"github.com/GreptimeTeam/gtctl/pkg/cmd/gtctl/cluster/connect/mysql"
"github.com/GreptimeTeam/gtctl/pkg/cmd/gtctl/cluster/connect/pg"
"github.com/GreptimeTeam/gtctl/pkg/deployer/k8s"
"github.com/GreptimeTeam/gtctl/pkg/logger"
)

type getClusterCliOptions struct {
Namespace string
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
}

func NewConnectCommand(l logger.Logger) *cobra.Command {
var options getClusterCliOptions
cmd := &cobra.Command{
Use: "connect",
Short: "Connect to a GreptimeDB cluster",
Long: `Connect to a GreptimeDB cluster`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("cluster name should be set")
}

k8sDeployer, err := k8s.NewDeployer(l)
if err != nil {
return err
}

var (
ctx = context.TODO()
clusterName = args[0]
namespace = options.Namespace
)

name := types.NamespacedName{
Namespace: options.Namespace,
Name: clusterName,
}.String()
cluster, err := k8sDeployer.GetGreptimeDBCluster(ctx, name, nil)
if err != nil && errors.IsNotFound(err) {
l.Errorf("cluster %s in %s not found\n", clusterName, namespace)
return nil
}
rawCluster, ok := cluster.Raw.(*greptimedbclusterv1alpha1.GreptimeDBCluster)
if !ok {
return fmt.Errorf("invalid cluster type")
}
dbType := cmd.Flag("p")
switch strings.ToLower(dbType.Value.String()) {
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
case "mysql":
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
err := mysql.ConnectCommand(rawCluster, l)
if err != nil {
_ = fmt.Errorf("error connecting to mysql: %v", err)
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
}
case "pg":
err := pg.ConnectCommand(rawCluster, l)
if err != nil {
_ = fmt.Errorf("error connecting to postgres: %v", err)
}
default:
return fmt.Errorf("database type not supported")
}
return nil
},
}
cmd.Flags().String("p", "mysql", "Specify a database")
cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", "default", "Namespace of GreptimeDB cluster.")
return cmd
}
111 changes: 111 additions & 0 deletions pkg/cmd/gtctl/cluster/connect/mysql/mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mysql

import (
"context"
"database/sql"
"os"
"os/exec"
"strconv"
"sync"

"github.com/go-sql-driver/mysql"

greptimedbclusterv1alpha1 "github.com/GreptimeTeam/greptimedb-operator/apis/v1alpha1"
"github.com/GreptimeTeam/gtctl/pkg/logger"
)

const (
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
ArgHost = "-h"
ArgPort = "-P"
MySQL = "mysql"
)

// ConnectCommand connects to a GreptimeDB cluster
func ConnectCommand(rawCluster *greptimedbclusterv1alpha1.GreptimeDBCluster, l logger.Logger) error {
return connect("127.0.0.1", strconv.Itoa(int(rawCluster.Spec.MySQLServicePort)), rawCluster.Name, l)
}

// connect connects to a GreptimeDB cluster
func connect(host, port, clusterName string, l logger.Logger) error {
waitGroup := sync.WaitGroup{}
cmd := exec.CommandContext(context.Background(), "kubectl", "port-forward", "-n", "default", "svc/"+clusterName+"-frontend", port+":"+port)
err := cmd.Start()
if err != nil {
l.Errorf("Error starting port-forwarding: %v", err)
return err
}
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
if err = cmd.Wait(); err != nil {
if err != nil && err.Error() != "signal: killed" {
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
l.V(1).Info("Shutting down port-forwarding successfully")
}
}
}()
for {
cfg := mysql.Config{
Net: "tcp",
Addr: "127.0.0.1:4002",
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
User: "",
Passwd: "",
DBName: "",
AllowNativePasswords: true,
}

db, err := sql.Open("mysql", cfg.FormatDSN())
if err != nil {
continue
}

_, err = db.Conn(context.Background())
if err != nil {
continue
}

err = db.Close()
if err != nil {
l.V(1).Infof("Error closing connection: %v", err)
return err
}
break
}

cmd = exec.Command(MySQL, ArgHost, host, ArgPort, port)
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err = cmd.Start()
if err != nil {
l.Errorf("Error starting mysql client: %v", err)
return err
}
if err = cmd.Wait(); err != nil {
l.Errorf("Error waiting for mysql client to finish: %v", err)
return err
}
// gracefully stop port-forwarding
err = cmd.Process.Kill()
if err != nil {
if err.Error() != "os: process already finished" {
sjcsjc123 marked this conversation as resolved.
Show resolved Hide resolved
l.V(1).Info("Shutting down port-forwarding successfully")
}
return err
}
waitGroup.Wait()
return nil
}
24 changes: 24 additions & 0 deletions pkg/cmd/gtctl/cluster/connect/pg/pg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pg

import (
greptimedbclusterv1alpha1 "github.com/GreptimeTeam/greptimedb-operator/apis/v1alpha1"
"github.com/GreptimeTeam/gtctl/pkg/logger"
)

func ConnectCommand(rawCluster *greptimedbclusterv1alpha1.GreptimeDBCluster, l logger.Logger) error {
return nil
}
Loading