rework err

This commit is contained in:
rmanach 2025-04-02 11:18:23 +02:00
parent 04e14928f3
commit 771a0e50bd
6 changed files with 54 additions and 62 deletions

View File

@ -2,6 +2,7 @@ package connection
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -20,6 +21,16 @@ type SSHConn struct {
var _ IConnection = (*SSHConn)(nil) var _ IConnection = (*SSHConn)(nil)
var (
ErrSSHDial = errors.New("unable to dial ssh addr")
ErrSSHConn = errors.New("unable to establish a new connection")
ErrSShCopy = errors.New("unable to copy file")
ErrSSHSession = errors.New("unable to open a new session")
ErrSSHReadPrivateKey = errors.New("unable to read private key")
ErrSSHParsePrivateKey = errors.New("unable to read private key")
ErrSSHExecute = errors.New("unable")
)
func NewSSHConn(addr, user string, port int, privkey string) (SSHConn, error) { func NewSSHConn(addr, user string, port int, privkey string) (SSHConn, error) {
var newconn SSHConn var newconn SSHConn
@ -28,20 +39,17 @@ func NewSSHConn(addr, user string, port int, privkey string) (SSHConn, error) {
conn, err := net.Dial("tcp", sshAddr) conn, err := net.Dial("tcp", sshAddr)
if err != nil { if err != nil {
log.Err(err).Str("addr", addr).Msg("unable to dial ssh addr") return newconn, fmt.Errorf("%w, addr=%s, err=%v", ErrSSHDial, addr, err)
return newconn, err
} }
c, err := os.ReadFile(privkey) c, err := os.ReadFile(privkey)
if err != nil { if err != nil {
log.Err(err).Str("private key", privkey).Msg("unable to read ssh private key") return newconn, fmt.Errorf("%w, privkey=%s, err=%v", ErrSSHReadPrivateKey, privkey, err)
return newconn, err
} }
sshPrivKey, err := ssh.ParsePrivateKey(c) sshPrivKey, err := ssh.ParsePrivateKey(c)
if err != nil { if err != nil {
log.Err(err).Str("private key", privkey).Msg("unable to parse ssh private key") return newconn, fmt.Errorf("%w, privkey=%s, err=%v", ErrSSHParsePrivateKey, privkey, err)
return newconn, err
} }
sshConfig := ssh.ClientConfig{ sshConfig := ssh.ClientConfig{
@ -54,14 +62,12 @@ func NewSSHConn(addr, user string, port int, privkey string) (SSHConn, error) {
sshConn, chNewChannel, chReq, err := ssh.NewClientConn(conn, sshAddr, &sshConfig) sshConn, chNewChannel, chReq, err := ssh.NewClientConn(conn, sshAddr, &sshConfig)
if err != nil { if err != nil {
log.Err(err).Str("addr", sshAddr).Msg("unable to establish a new connection to the swarm") return newconn, fmt.Errorf("%w, addr=%s, err=%v", ErrSSHConn, sshAddr, err)
return newconn, err
} }
sshClient := ssh.NewClient(sshConn, chNewChannel, chReq) sshClient := ssh.NewClient(sshConn, chNewChannel, chReq)
newconn.client = sshClient newconn.client = sshClient
log.Info().Str("addr", addr).Int("port", port).Msg("ssh connection sucessfully initialized")
return newconn, nil return newconn, nil
} }
@ -72,21 +78,18 @@ func (c *SSHConn) Close() error {
func (c *SSHConn) CopyFile(src, dest string) error { func (c *SSHConn) CopyFile(src, dest string) error {
sshSession, err := c.client.NewSession() sshSession, err := c.client.NewSession()
if err != nil { if err != nil {
log.Err(err).Str("addr", c.addr).Msg("unable to open an ssh session") return fmt.Errorf("%w, addr=%s, err=%v", ErrSSHSession, c.addr, err)
return err
} }
defer sshSession.Close() defer sshSession.Close()
fileInfo, err := os.Stat(src) fileInfo, err := os.Stat(src)
if err != nil { if err != nil {
log.Err(err).Str("file", src).Msg("unable to stat scp source file") return fmt.Errorf("unable to stat scp source file src=%s, err=%v", src, err)
return err
} }
file, err := os.Open(src) file, err := os.Open(src)
if err != nil { if err != nil {
log.Err(err).Str("file", src).Msg("unable to open scp source file") return fmt.Errorf("unable to open scp source file src=%s, err=%v", src, err)
return err
} }
defer file.Close() defer file.Close()
@ -97,7 +100,7 @@ func (c *SSHConn) CopyFile(src, dest string) error {
fmt.Fprintf(w, "C0644 %d %s\n", fileInfo.Size(), filepath.Base(dest)) fmt.Fprintf(w, "C0644 %d %s\n", fileInfo.Size(), filepath.Base(dest))
if _, err := io.Copy(w, file); err != nil { if _, err := io.Copy(w, file); err != nil {
log.Err(err).Str("src", src).Str("dest", dest).Msg("unable to scp src to dest") log.Debug().Err(err).Str("src", src).Str("dest", dest).Msg("unable to scp src to dest")
return return
} }
@ -105,27 +108,23 @@ func (c *SSHConn) CopyFile(src, dest string) error {
}() }()
if err := sshSession.Run(fmt.Sprintf("scp -t %s", dest)); err != nil { if err := sshSession.Run(fmt.Sprintf("scp -t %s", dest)); err != nil {
log.Err(err).Str("addr", c.addr).Str("dest", dest).Msg("unable to run scp command") return fmt.Errorf("%w, addr=%s, src=%s, dest=%s, err=%v", ErrSShCopy, c.addr, src, dest, err)
return err
} }
log.Info().Str("src", src).Str("dest", dest).Msg("file successfully uploaded")
return nil return nil
} }
func (c *SSHConn) Execute(cmd string) (string, error) { func (c *SSHConn) Execute(cmd string) (string, error) {
sshSession, err := c.client.NewSession() sshSession, err := c.client.NewSession()
if err != nil { if err != nil {
log.Err(err).Str("addr", c.addr).Msg("unable to open an ssh session") return "", fmt.Errorf("%w, addr=%s, err=%v", ErrSSHSession, c.addr, err)
return "", err
} }
defer sshSession.Close() defer sshSession.Close()
var buf bytes.Buffer var buf bytes.Buffer
sshSession.Stdout = &buf sshSession.Stdout = &buf
if err := sshSession.Run(cmd); err != nil { if err := sshSession.Run(cmd); err != nil {
log.Err(err).Str("addr", c.addr).Str("command", cmd).Msg("unable to execute an ssh command") return "", fmt.Errorf("%w, addr=%s, cmd=%s, err=%v", ErrSSHExecute, c.addr, cmd, err)
return "", err
} }
return "", nil return "", nil

View File

@ -6,11 +6,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/rs/zerolog/log"
"gitea.thegux.fr/hmdeploy/connection" "gitea.thegux.fr/hmdeploy/connection"
"gitea.thegux.fr/hmdeploy/docker" "gitea.thegux.fr/hmdeploy/docker"
"gitea.thegux.fr/hmdeploy/models" "gitea.thegux.fr/hmdeploy/models"
"github.com/rs/zerolog/log"
) )
type SwarmDeployer struct { type SwarmDeployer struct {
@ -57,12 +56,16 @@ func (sd *SwarmDeployer) clean() (err error) {
func (sd *SwarmDeployer) Deploy() error { func (sd *SwarmDeployer) Deploy() error {
defer sd.clean() defer sd.clean()
if sd.project.ImageName != "" { if imageName := sd.project.ImageName; imageName != "" {
tarFile, err := sd.dcli.Save(sd.project.ImageName, sd.project.Dir) log.Info().Str("image", imageName).Msg("saving image for transfert...")
tarFile, err := sd.dcli.Save(imageName, sd.project.Dir)
if err != nil { if err != nil {
return err return err
} }
log.Info().Str("image", imageName).Str("dir", sd.project.Dir).Msg("image saved successfully")
defer os.Remove(tarFile) defer os.Remove(tarFile)
tarFileBase := filepath.Base(tarFile) tarFileBase := filepath.Base(tarFile)
@ -91,6 +94,5 @@ func (sd *SwarmDeployer) Deploy() error {
return err return err
} }
log.Info().Msg("project deployed successfully on swarm")
return nil return nil
} }

View File

@ -1,18 +1,19 @@
package docker package docker
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"github.com/rs/zerolog/log"
) )
type IClient interface { type IClient interface {
Save(imageName, dest string) (string, error) Save(imageName, dest string) (string, error)
} }
var ErrDockerClientSave = errors.New("unable to save image into tar")
type Client struct{} type Client struct{}
var _ IClient = (*Client)(nil) var _ IClient = (*Client)(nil)
@ -24,12 +25,11 @@ func NewClient() Client {
func (c *Client) Save(imageName, dest string) (string, error) { func (c *Client) Save(imageName, dest string) (string, error) {
destInfo, err := os.Stat(dest) destInfo, err := os.Stat(dest)
if err != nil { if err != nil {
log.Err(err).Str("dest", dest).Msg("unable to stat dest directory") return "", fmt.Errorf("unable to stat file, dir=%s, err=%v", dest, err)
return "", err
} }
if !destInfo.IsDir() { if !destInfo.IsDir() {
log.Err(err).Str("dest", dest).Msg("dest directory must be a directory") return "", fmt.Errorf("dest file must be a directory, dir=%s, err=%v", dest, err)
} }
tarFile := fmt.Sprintf("%s.tar", imageName) tarFile := fmt.Sprintf("%s.tar", imageName)
@ -37,10 +37,8 @@ func (c *Client) Save(imageName, dest string) (string, error) {
cmd := exec.Command("docker", "save", "-o", tarFile, imageName) cmd := exec.Command("docker", "save", "-o", tarFile, imageName)
cmd.Dir = dest cmd.Dir = dest
if _, err := cmd.Output(); err != nil { if _, err := cmd.Output(); err != nil {
log.Err(err).Str("image", imageName).Str("dest", dest).Msg("unable to save image into tar") return "", fmt.Errorf("%w, dir=%s, image=%s, err=%v", ErrDockerClientSave, dest, imageName, err)
return dest, err
} }
log.Info().Str("image", imageName).Str("dest", dest).Msg("image successfully saved")
return filepath.Join(dest, tarFile), nil return filepath.Join(dest, tarFile), nil
} }

View File

@ -59,17 +59,20 @@ func main() {
project, err := models.ProjectFromDir(*projectDir) project, err := models.ProjectFromDir(*projectDir)
if err != nil { if err != nil {
os.Exit(1) log.Fatal().Str("dir", *projectDir).Err(err).Msg("unable to init project from directory")
} }
log.Info().Str("dir", project.Dir).Str("name", project.Name).Msg("project initialized with success")
dcli := docker.NewClient() dcli := docker.NewClient()
sd, err := deployers.NewSwarmDeployer(ctx, &dcli, swarmNet, &project) sd, err := deployers.NewSwarmDeployer(ctx, &dcli, swarmNet, &project)
if err != nil { if err != nil {
os.Exit(1) log.Fatal().Err(err).Msg("unable to init swarm deployer")
} }
if err := sd.Deploy(); err != nil { if err := sd.Deploy(); err != nil {
os.Exit(1) log.Fatal().Err(err).Msg("unable to deploy project")
} }
log.Info().Str("name", project.Name).Msg("project deployed successfully")
} }

View File

@ -2,8 +2,6 @@ package models
import ( import (
"net" "net"
"github.com/rs/zerolog/log"
) )
type HMNetInfo struct { type HMNetInfo struct {
@ -28,7 +26,6 @@ type HMMap struct {
func (hm *HMMap) GetSwarmNetInfo() *HMNetInfo { func (hm *HMMap) GetSwarmNetInfo() *HMNetInfo {
data, ok := hm.VM["swarm"] data, ok := hm.VM["swarm"]
if !ok { if !ok {
log.Error().Msg("unable to get swarm net info, check your configuration")
return nil return nil
} }

View File

@ -21,11 +21,7 @@ const (
ConfFile = "hmdeploy.json" ConfFile = "hmdeploy.json"
) )
var ( var ErrProjectConfFile = errors.New("project error")
ErrInvalidEnvFile = errors.New("unable to stat .env file")
ErrInvalidComposeFile = errors.New("unable to stat compose file")
ErrInvalidNginxFile = errors.New("unable to stat nginx file")
)
func getFileInfo(baseDir, filePath string) (fs.FileInfo, error) { func getFileInfo(baseDir, filePath string) (fs.FileInfo, error) {
var fInf fs.FileInfo var fInf fs.FileInfo
@ -33,14 +29,14 @@ func getFileInfo(baseDir, filePath string) (fs.FileInfo, error) {
filePath = filepath.Clean(filePath) filePath = filepath.Clean(filePath)
filePath = filepath.Join(baseDir, filePath) filePath = filepath.Join(baseDir, filePath)
composePath, err := filepath.Abs(filePath) fileAbsPath, err := filepath.Abs(filePath)
if err != nil { if err != nil {
return fInf, fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) return fInf, fmt.Errorf("%w, file=%s, err=%v", ErrProjectConfFile, fileAbsPath, err)
} }
fInf, err = os.Stat(composePath) fInf, err = os.Stat(fileAbsPath)
if err != nil { if err != nil {
return fInf, fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) return fInf, fmt.Errorf("%w, unable to stat file=%s, err=%v", ErrProjectConfFile, fileAbsPath, err)
} }
return fInf, nil return fInf, nil
@ -65,23 +61,23 @@ type Project struct {
func (p *Project) validate() error { func (p *Project) validate() error {
cfs, err := getFileInfo(p.Dir, p.Deps.ComposeFile) cfs, err := getFileInfo(p.Dir, p.Deps.ComposeFile)
if err != nil { if err != nil {
return fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) return err
} }
p.Deps.ComposeFileInfo = cfs p.Deps.ComposeFileInfo = cfs
if p.Deps.EnvFile != "" { if p.Deps.EnvFile != "" {
efs, err := getFileInfo(p.Dir, p.Deps.EnvFile) efs, err := getFileInfo(p.Dir, p.Deps.EnvFile)
if err != nil { if err != nil {
return fmt.Errorf("%w, %v", ErrInvalidEnvFile, err) return err
} }
p.Deps.EnvFileInfo = efs p.Deps.EnvFileInfo = efs
} else { } else {
log.Warn().Msg("no .env file provided, hoping one is set elsewhere...") log.Warn().Msg("no .env file provided, hoping one it's set elsewhere...")
} }
nfs, err := getFileInfo(p.Dir, p.Deps.EnvFile) nfs, err := getFileInfo(p.Dir, p.Deps.NginxFile)
if err != nil { if err != nil {
return fmt.Errorf("%w, %v", ErrInvalidNginxFile, err) return err
} }
p.Deps.NginxFileInfo = nfs p.Deps.NginxFileInfo = nfs
@ -96,18 +92,15 @@ func ProjectFromDir(dir string) (Project, error) {
content, err := os.ReadFile(filepath.Join(dir, ConfFile)) content, err := os.ReadFile(filepath.Join(dir, ConfFile))
if err != nil { if err != nil {
log.Err(err).Str("dir", dir).Str("conf", ConfFile).Msg("unable to read conf file") return p, fmt.Errorf("%w, unable to read conf file=%s, err=%v", ErrProjectConfFile, ConfFile, err)
return p, err
} }
if err := json.Unmarshal(content, &p); err != nil { if err := json.Unmarshal(content, &p); err != nil {
log.Err(err).Str("dir", dir).Str("conf", ConfFile).Msg("unable to parse conf file") return p, fmt.Errorf("%w, unable to parse conf file=%s, err=%v", ErrProjectConfFile, ConfFile, err)
return p, err
} }
if err := p.validate(); err != nil { if err := p.validate(); err != nil {
log.Err(err).Str("dir", dir).Msg("unable to validate project") return p, fmt.Errorf("%w, unable to validate project, name=%s, dir=%s, err=%v", ErrProjectConfFile, p.Name, p.Dir, err)
return p, err
} }
return p, nil return p, nil