2

I'm trying to develop a functionality on a Go tool that I wrote to facilitate AWS CLI usage. Basically, the goal is to dump a MySQL database through AWS SSM by using port forwarding.

First of all, it works fine when I do the commands manually.

aws ssm start-session --document-name SSM-Document --target i-target --region eu-west-3 --parameters '{"host":["myhost.eu-west-3.rds.amazonaws.com"]}'

This command above gives that kind of output:

Starting session with SessionId:
Port 3306 opened for sessionId 
Waiting for connections...

In an other shell, locally, on my computer, not on RDS instance, I can do the dump without any problem:

mysqldump -h 127.0.0.1 -u user -p password dbname --result-file=dump.sql

and that's it. The dump file is generated.

But when I try to do something similar in my Golang code I have the following error:

2023/07/18 09:51:28 SSM session started successfully. User: i-target, Instance ID: , Session ID:, Connection Status: Connected
2023/07/18 09:51:28 Executing mysqldump command: /opt/homebrew/bin/mysqldump -h 127.0.0.1 -P3306 -u user -p password dbname --result-file=dump.sql
2023/07/18 09:51:28 mysqldump command failed with error: exit status 2
mysqldump: Got error: 2003: Can't connect to MySQL server on '127.0.0.1:3306' (61) when trying to connect

2023/07/18 09:51:28 SSM session status: Connected

Here is the Golang code:

func DumpDatabase(ctx context.Context, host, appServerId, port, user, password, dbName, fileName string) error {
    utils.LoadAWSEnv()
    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    // Create an RDS service client
    svc := rds.New(sess)

    // Get the endpoint for the specified RDS instance
    params := &rds.DescribeDBInstancesInput{
        DBInstanceIdentifier: aws.String(host),
    }
    rdsResp, err := svc.DescribeDBInstancesWithContext(ctx, params)
    if err != nil {
        return fmt.Errorf("error describing DB instance: %v", err)
    }
    if len(rdsResp.DBInstances) == 0 {
        return fmt.Errorf("DB instance not found")
    }
    endpoint := rdsResp.DBInstances[0].Endpoint.Address

    // Create a wait group to wait for the SSM session and mysqldump command to complete
    var wg sync.WaitGroup
    wg.Add(2)

    // Create a context to manage the SSM session and mysqldump command
    ssmCtx, ssmCancel := context.WithCancel(ctx)
    defer ssmCancel()

    // Create a channel to signal when the SSM session has started
    ssmStarted := make(chan error)

    // Create a channel to signal when the mysqldump command has completed
    mysqldumpDone := make(chan error)

    // Declare the SSM service client and SSM session output variables
    var ssmSvc *ssm.SSM
    var ssmSession *ssm.Session

    // Start the SSM session in a goroutine
    go func() {
        defer wg.Done()

        // Create an SSM service client
        ssmSvc = ssm.New(sess)

        // Start the SSM session
        _, err := ssmSvc.StartSessionWithContext(ssmCtx, &ssm.StartSessionInput{
            DocumentName: aws.String("SSM-Document"),
            Target:       aws.String(appServerId),
            Parameters:   map[string][]*string{"host": {aws.String(*endpoint)}},
        })
        if err != nil {
            log.Println("Error starting SSM session:", err)
            mysqldumpDone <- fmt.Errorf("error starting SSM session: %v", err)
            return
        }

        // Wait for the SSM session to start
        for {
            output, err := ssmSvc.DescribeSessionsWithContext(ssmCtx, &ssm.DescribeSessionsInput{
                State: aws.String("Active"),
            })
            if err != nil {
                log.Println("Error describing SSM session:", err)
                mysqldumpDone <- fmt.Errorf("error describing SSM session: %v", err)
                return
            }

            if len(output.Sessions) == 0 {
                log.Println("SSM session not found")
                mysqldumpDone <- fmt.Errorf("SSM session not found")
                return
            }

            if *output.Sessions[0].Status == "Connected" {
                ssmSession = output.Sessions[0]
                log.Printf("SSM session started successfully. User: %s, Instance ID: %s, Session ID: %s, Connection Status: %s\n", *ssmSession.Target, *ssmSession.SessionId, *ssmSession.SessionId, *ssmSession.Status)
                break
            }

            log.Println("Waiting for SSM session to start...")
            time.Sleep(5 * time.Second)
        }

        // Signal that the SSM session has started
        ssmStarted <- nil
    }()

    // Start the mysqldump command in a goroutine
    go func() {
        defer wg.Done()

        // Wait for the SSM session to start
        <-ssmStarted

        // Start the mysqldump command

        dumpCmd := exec.CommandContext(ssmCtx, "mysqldump", "-h127.0.0.1", "-P"+port, "-u"+user, "-p"+password, dbName, "--result-file="+fileName)
        //dumpCmd.Env = append(os.Environ(), "MYSQL_TCP_PORT="+port)
        log.Println("Executing mysqldump command:", dumpCmd.String())
        output, err := dumpCmd.CombinedOutput()
        if err != nil {
            log.Println("mysqldump command failed with error:", err)
            log.Println("output:", string(output))
            log.Println("SSM session status:", *ssmSession.Status)

            // Signal that the mysqldump command has completed with an error
            mysqldumpDone <- fmt.Errorf("mysqldump command failed with error: %v, SSM session status: %s", err, *ssmSession.Status)

            return
        }

        // Signal that the mysqldump command has completed successfully
        mysqldumpDone <- nil
    }()

    // Wait for the SSM session and mysqldump command to complete
    wg.Wait()

    // Check if the mysqldump command completed with an error
    err = <-mysqldumpDone
    if err != nil {
        return err
    }

    return nil
}

Initially I thought that my issue was related to the SSM session that was closed before doing the actual mysqldump command but it seems that the session is still active even after the error. Or maybe the issue is that the mysqldump command is not running as a "detached" process.

916
  • 21
  • 2
  • When setting up the `mysqldump` cmd, shouldn't you be using `endpoint` rather than `127.0.0.1`? – Gavin Jul 18 '23 at 13:43
  • I don't think so since the `mysqldump` command should be executed on the user's computer, not on the RDS instance. – 916 Jul 18 '23 at 14:42
  • Can you clarify «dump a MySQL database through AWS SSM by using port forwarding» fo me then? I would think you're after running `mysqldump` "in the cloud", and now it sounds like you want to run it locally but make it _send_ the dump "to" an "AWS SSM session", whatever that means (I'm not familiar with the concept). If `mysqldump` should run on the user's local host, the whole AWS SSM is out of the question: you should first figure out why it fails to start, and then think why it does not send its data to that thing. – kostix Jul 18 '23 at 18:24
  • Hello @kostix, thanks for your reply! That's what I show when I do the manual commands at the beginning of my post. From my computer, I connect to the RDS instance with an AWS SSM document dedicated to RDS stuff with portNumber and localPortNumber (3306). Then, when the connection is successful, it waits for another connection. I open a new shell/tab locally on my computer and I do the `mysqldump -h 127.0.0.1 -u user -p password dbname --result-file=dmp.sql` command to make the MySQL dump. The connection to the instance is working fine in the code, but somehow the dump is not working. – 916 Jul 18 '23 at 19:30
  • 1
    Your `Executing mysqldump command:` log output doesn't match how you're setting up the command (there are no spaces in the command arguments). Maybe try: `exec.CommandContext(ssmCtx, "mysqldump", "-h", "127.0.0.1", "-P", port, "-u", user, "-p", password, dbName, "--result-file="+fileName)` – Gavin Jul 20 '23 at 16:48
  • Hello @gavin, yes it doesn't match because I had to anonymize the command a bit. But the command works even with no spaces. – 916 Jul 24 '23 at 07:42

0 Answers0