Skip to content

Commit

Permalink
refactor: Enhance file transfer and improve Nginx configuration handling
Browse files Browse the repository at this point in the history
  • Loading branch information
yarlson committed Oct 19, 2024
1 parent 390c606 commit 2cd50da
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 53 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ aerie setup
```

This command:

1. Installs essential software and Docker
2. Configures the server firewall
3. Creates a new user and adds them to the Docker group
Expand Down Expand Up @@ -101,10 +102,11 @@ Aerie uses a sophisticated deployment process to ensure your application is alwa
4. **Image Pulling**: The latest versions of your Docker images are pulled to ensure you're deploying the most recent code.

5. **Zero-Downtime Deployment**: For each service:
- A new container is started with the updated image and configuration.
- Health checks are performed to ensure the new container is ready.
- Once healthy, traffic is instantly switched to the new container.
- The old container is gracefully stopped and removed.

- A new container is started with the updated image and configuration.
- Health checks are performed to ensure the new container is ready.
- Once healthy, traffic is instantly switched to the new container.
- The old container is gracefully stopped and removed.

This process ensures that your application remains available throughout the update.

Expand Down
5 changes: 3 additions & 2 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package cmd

import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
"github.com/yarlson/aerie/pkg/config"
"github.com/yarlson/aerie/pkg/deployment"
"github.com/yarlson/aerie/pkg/logfmt"
"github.com/yarlson/aerie/pkg/ssh"
"os"
"path/filepath"
)

var deployCmd = &cobra.Command{
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ go 1.23
toolchain go1.23.1

require (
github.com/bramvdbogaerde/go-scp v1.5.0
github.com/briandowns/spinner v1.23.1
github.com/fatih/color v1.17.0
github.com/go-playground/validator/v10 v10.22.1
github.com/joho/godotenv v1.5.1
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.28.0
Expand All @@ -21,7 +23,6 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/bramvdbogaerde/go-scp v1.5.0 h1:a9BinAjTfQh273eh7vd3qUgmBC+bx+3TRDtkZWmIpzM=
github.com/bramvdbogaerde/go-scp v1.5.0/go.mod h1:on2aH5AxaFb2G0N5Vsdy6B0Ml7k9HuHSwfo1y0QzAbQ=
github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650=
github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
Expand Down
2 changes: 1 addition & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package config

import (
"fmt"
"github.com/joho/godotenv"
"os"
"path/filepath"
"strings"
"time"

"github.com/go-playground/validator/v10"
"github.com/joho/godotenv"
"gopkg.in/yaml.v3"
)

Expand Down
42 changes: 25 additions & 17 deletions pkg/deployment/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"

"github.com/yarlson/aerie/pkg/proxy"

"github.com/yarlson/aerie/pkg/config"
"github.com/yarlson/aerie/pkg/logfmt"
"github.com/yarlson/aerie/pkg/proxy"
)

const (
Expand All @@ -22,6 +23,7 @@ const (

type Executor interface {
RunCommand(ctx context.Context, command string, args ...string) (io.Reader, error)
CopyFile(ctx context.Context, from, to string) error
}

type Deployment struct {
Expand Down Expand Up @@ -74,7 +76,7 @@ func (d *Deployment) StartProxy(project string, cfg *config.Config, network stri
Port: 80,
Volumes: []string{
projectPath + "/:/etc/nginx/ssl",
configPath + ":/etc/nginx/conf.d/default.conf",
configPath + ":/etc/nginx/conf.d",
},
EnvVars: []config.EnvVar{
{
Expand Down Expand Up @@ -339,18 +341,6 @@ func (d *Deployment) runCommand(ctx context.Context, command string, args ...str
return result, nil
}

func (d *Deployment) copyTextFile(sourceText, destination string) error {
escapedSource := strings.ReplaceAll(sourceText, "'", "'\\''")
command := "bash"
args := []string{"-c", fmt.Sprintf("echo '%s' > %s", escapedSource, destination)}
_, err := d.runCommand(context.Background(), command, args...)
if err != nil {
return fmt.Errorf("failed to copy text file: %w", err)
}

return nil
}

func (d *Deployment) makeProjectFolder(projectName string) error {
projectPath, err := d.projectFolder(projectName)
if err != nil {
Expand Down Expand Up @@ -387,8 +377,26 @@ func (d *Deployment) prepareNginxConfig(cfg *config.Config, projectPath string)
return "", fmt.Errorf("failed to generate nginx config: %w", err)
}

configPath := filepath.Join(projectPath, "nginx.conf")
return configPath, d.copyTextFile(nginxConfig, configPath)
nginxConfig = strings.TrimSpace(nginxConfig)
logfmt.Info(nginxConfig)

configPath := filepath.Join(projectPath, "nginx")
_, err = d.runCommand(context.Background(), "mkdir", "-p", configPath)
if err != nil {
return "", fmt.Errorf("failed to create nginx config directory: %w", err)
}

tmpFile, err := os.CreateTemp("", "nginx-config-*.conf")
if err != nil {
return "", fmt.Errorf("failed to create temporary file: %w", err)
}
defer os.Remove(tmpFile.Name())

if _, err := tmpFile.WriteString(nginxConfig); err != nil {
return "", fmt.Errorf("failed to write nginx config to temporary file: %w", err)
}

return configPath, d.executor.CopyFile(context.Background(), tmpFile.Name(), filepath.Join(configPath, "default.conf"))
}

func ServiceHash(service *config.Service) (string, error) {
Expand Down
33 changes: 6 additions & 27 deletions pkg/deployment/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"time"
Expand Down Expand Up @@ -47,6 +46,12 @@ func (e *LocalExecutor) RunCommand(ctx context.Context, command string, args ...
return bytes.NewReader(combinedOutput.Bytes()), nil
}

func (e *LocalExecutor) CopyFile(ctx context.Context, src, dst string) error {
cmd := exec.CommandContext(ctx, "cp", src, dst)

return cmd.Run()
}

func (suite *DeploymentTestSuite) SetupSuite() {
suite.network = "aerie-test-network"
_ = exec.Command("docker", "network", "create", suite.network).Run()
Expand Down Expand Up @@ -276,32 +281,6 @@ func (suite *DeploymentTestSuite) TestUpdateService() {
})
}

func (suite *DeploymentTestSuite) TestCopyTextFile() {
tmpDir, err := os.MkdirTemp("", "copyTextFile-test")
suite.Require().NoError(err)
defer os.RemoveAll(tmpDir)

sourceContent := "This is a test file\nWith multiple lines\nAnd some 'quoted' text"
destPath := filepath.Join(tmpDir, "destination.txt")

err = suite.updater.copyTextFile(sourceContent, destPath)
suite.Require().NoError(err)

destContent, err := os.ReadFile(destPath)
suite.Require().NoError(err)
suite.Equal(strings.TrimSpace(sourceContent), strings.TrimSpace(string(destContent)))

specialContent := "Line with 'single quotes'\nLine with \"double quotes\"\nLine with $dollar signs"
specialDestPath := filepath.Join(tmpDir, "special_destination.txt")

err = suite.updater.copyTextFile(specialContent, specialDestPath)
suite.Require().NoError(err)

specialDestContent, err := os.ReadFile(specialDestPath)
suite.Require().NoError(err)
suite.Equal(strings.TrimSpace(specialContent), strings.TrimSpace(string(specialDestContent)))
}

func (suite *DeploymentTestSuite) TestMakeProjectFolder() {
projectName := "test-project"

Expand Down
20 changes: 19 additions & 1 deletion pkg/ssh/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"strings"
"time"

"github.com/bramvdbogaerde/go-scp"
"github.com/briandowns/spinner"

"github.com/fatih/color"
"golang.org/x/crypto/ssh"
)
Expand Down Expand Up @@ -144,6 +144,24 @@ func (c *Client) RunCommand(ctx context.Context, command string, args ...string)
return bytes.NewReader(output.Bytes()), nil
}

func (c *Client) CopyFile(ctx context.Context, src, dst string) error {
if err := c.ensureConnected(); err != nil {
return err
}

client, err := scp.NewClientBySSH(c.sshClient)
if err != nil {
return fmt.Errorf("failed to create SCP client: %w", err)
}

file, err := os.Open(src)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}

return client.CopyFile(ctx, file, dst, "0644")
}

func (c *Client) RunCommandWithProgress(ctx context.Context, initialMsg, completeMsg string, commands []string) error {
s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
s.Suffix = " " + initialMsg
Expand Down

0 comments on commit 2cd50da

Please sign in to comment.