From 771a0e50bd01b30cef918660c80c2794e227b159 Mon Sep 17 00:00:00 2001 From: rmanach Date: Wed, 2 Apr 2025 11:18:23 +0200 Subject: [PATCH] rework err --- connection/ssh_connection.go | 45 ++++++++++++++++++------------------ deployers/swarm.go | 12 ++++++---- docker/client.go | 14 +++++------ main.go | 9 +++++--- models/hm.go | 3 --- models/project.go | 33 +++++++++++--------------- 6 files changed, 54 insertions(+), 62 deletions(-) diff --git a/connection/ssh_connection.go b/connection/ssh_connection.go index c93a892..a981cf3 100644 --- a/connection/ssh_connection.go +++ b/connection/ssh_connection.go @@ -2,6 +2,7 @@ package connection import ( "bytes" + "errors" "fmt" "io" "net" @@ -20,6 +21,16 @@ type SSHConn struct { 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) { var newconn SSHConn @@ -28,20 +39,17 @@ func NewSSHConn(addr, user string, port int, privkey string) (SSHConn, error) { conn, err := net.Dial("tcp", sshAddr) if err != nil { - log.Err(err).Str("addr", addr).Msg("unable to dial ssh addr") - return newconn, err + return newconn, fmt.Errorf("%w, addr=%s, err=%v", ErrSSHDial, addr, err) } c, err := os.ReadFile(privkey) if err != nil { - log.Err(err).Str("private key", privkey).Msg("unable to read ssh private key") - return newconn, err + return newconn, fmt.Errorf("%w, privkey=%s, err=%v", ErrSSHReadPrivateKey, privkey, err) } sshPrivKey, err := ssh.ParsePrivateKey(c) if err != nil { - log.Err(err).Str("private key", privkey).Msg("unable to parse ssh private key") - return newconn, err + return newconn, fmt.Errorf("%w, privkey=%s, err=%v", ErrSSHParsePrivateKey, privkey, err) } 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) if err != nil { - log.Err(err).Str("addr", sshAddr).Msg("unable to establish a new connection to the swarm") - return newconn, err + return newconn, fmt.Errorf("%w, addr=%s, err=%v", ErrSSHConn, sshAddr, err) } sshClient := ssh.NewClient(sshConn, chNewChannel, chReq) newconn.client = sshClient - log.Info().Str("addr", addr).Int("port", port).Msg("ssh connection sucessfully initialized") return newconn, nil } @@ -72,21 +78,18 @@ func (c *SSHConn) Close() error { func (c *SSHConn) CopyFile(src, dest string) error { sshSession, err := c.client.NewSession() if err != nil { - log.Err(err).Str("addr", c.addr).Msg("unable to open an ssh session") - return err + return fmt.Errorf("%w, addr=%s, err=%v", ErrSSHSession, c.addr, err) } defer sshSession.Close() fileInfo, err := os.Stat(src) if err != nil { - log.Err(err).Str("file", src).Msg("unable to stat scp source file") - return err + return fmt.Errorf("unable to stat scp source file src=%s, err=%v", src, err) } file, err := os.Open(src) if err != nil { - log.Err(err).Str("file", src).Msg("unable to open scp source file") - return err + return fmt.Errorf("unable to open scp source file src=%s, err=%v", src, err) } 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)) 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 } @@ -105,27 +108,23 @@ func (c *SSHConn) CopyFile(src, dest string) error { }() 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 err + return fmt.Errorf("%w, addr=%s, src=%s, dest=%s, err=%v", ErrSShCopy, c.addr, src, dest, err) } - log.Info().Str("src", src).Str("dest", dest).Msg("file successfully uploaded") return nil } func (c *SSHConn) Execute(cmd string) (string, error) { sshSession, err := c.client.NewSession() if err != nil { - log.Err(err).Str("addr", c.addr).Msg("unable to open an ssh session") - return "", err + return "", fmt.Errorf("%w, addr=%s, err=%v", ErrSSHSession, c.addr, err) } defer sshSession.Close() var buf bytes.Buffer sshSession.Stdout = &buf 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 "", err + return "", fmt.Errorf("%w, addr=%s, cmd=%s, err=%v", ErrSSHExecute, c.addr, cmd, err) } return "", nil diff --git a/deployers/swarm.go b/deployers/swarm.go index eec64fe..90223e4 100644 --- a/deployers/swarm.go +++ b/deployers/swarm.go @@ -6,11 +6,10 @@ import ( "os" "path/filepath" - "github.com/rs/zerolog/log" - "gitea.thegux.fr/hmdeploy/connection" "gitea.thegux.fr/hmdeploy/docker" "gitea.thegux.fr/hmdeploy/models" + "github.com/rs/zerolog/log" ) type SwarmDeployer struct { @@ -57,12 +56,16 @@ func (sd *SwarmDeployer) clean() (err error) { func (sd *SwarmDeployer) Deploy() error { defer sd.clean() - if sd.project.ImageName != "" { - tarFile, err := sd.dcli.Save(sd.project.ImageName, sd.project.Dir) + if imageName := sd.project.ImageName; imageName != "" { + log.Info().Str("image", imageName).Msg("saving image for transfert...") + + tarFile, err := sd.dcli.Save(imageName, sd.project.Dir) if err != nil { return err } + log.Info().Str("image", imageName).Str("dir", sd.project.Dir).Msg("image saved successfully") + defer os.Remove(tarFile) tarFileBase := filepath.Base(tarFile) @@ -91,6 +94,5 @@ func (sd *SwarmDeployer) Deploy() error { return err } - log.Info().Msg("project deployed successfully on swarm") return nil } diff --git a/docker/client.go b/docker/client.go index 81c8cb5..3c1b658 100644 --- a/docker/client.go +++ b/docker/client.go @@ -1,18 +1,19 @@ package docker import ( + "errors" "fmt" "os" "os/exec" "path/filepath" - - "github.com/rs/zerolog/log" ) type IClient interface { Save(imageName, dest string) (string, error) } +var ErrDockerClientSave = errors.New("unable to save image into tar") + type Client struct{} var _ IClient = (*Client)(nil) @@ -24,12 +25,11 @@ func NewClient() Client { func (c *Client) Save(imageName, dest string) (string, error) { destInfo, err := os.Stat(dest) if err != nil { - log.Err(err).Str("dest", dest).Msg("unable to stat dest directory") - return "", err + return "", fmt.Errorf("unable to stat file, dir=%s, err=%v", dest, err) } 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) @@ -37,10 +37,8 @@ func (c *Client) Save(imageName, dest string) (string, error) { cmd := exec.Command("docker", "save", "-o", tarFile, imageName) cmd.Dir = dest if _, err := cmd.Output(); err != nil { - log.Err(err).Str("image", imageName).Str("dest", dest).Msg("unable to save image into tar") - return dest, err + return "", fmt.Errorf("%w, dir=%s, image=%s, err=%v", ErrDockerClientSave, dest, imageName, err) } - log.Info().Str("image", imageName).Str("dest", dest).Msg("image successfully saved") return filepath.Join(dest, tarFile), nil } diff --git a/main.go b/main.go index c44a3ed..4230014 100644 --- a/main.go +++ b/main.go @@ -59,17 +59,20 @@ func main() { project, err := models.ProjectFromDir(*projectDir) 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() sd, err := deployers.NewSwarmDeployer(ctx, &dcli, swarmNet, &project) if err != nil { - os.Exit(1) + log.Fatal().Err(err).Msg("unable to init swarm deployer") } 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") } diff --git a/models/hm.go b/models/hm.go index d5e2305..1635daf 100644 --- a/models/hm.go +++ b/models/hm.go @@ -2,8 +2,6 @@ package models import ( "net" - - "github.com/rs/zerolog/log" ) type HMNetInfo struct { @@ -28,7 +26,6 @@ type HMMap struct { func (hm *HMMap) GetSwarmNetInfo() *HMNetInfo { data, ok := hm.VM["swarm"] if !ok { - log.Error().Msg("unable to get swarm net info, check your configuration") return nil } diff --git a/models/project.go b/models/project.go index 253fbdf..4224b9d 100644 --- a/models/project.go +++ b/models/project.go @@ -21,11 +21,7 @@ const ( ConfFile = "hmdeploy.json" ) -var ( - ErrInvalidEnvFile = errors.New("unable to stat .env file") - ErrInvalidComposeFile = errors.New("unable to stat compose file") - ErrInvalidNginxFile = errors.New("unable to stat nginx file") -) +var ErrProjectConfFile = errors.New("project error") func getFileInfo(baseDir, filePath string) (fs.FileInfo, error) { var fInf fs.FileInfo @@ -33,14 +29,14 @@ func getFileInfo(baseDir, filePath string) (fs.FileInfo, error) { filePath = filepath.Clean(filePath) filePath = filepath.Join(baseDir, filePath) - composePath, err := filepath.Abs(filePath) + fileAbsPath, err := filepath.Abs(filePath) 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 { - 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 @@ -65,23 +61,23 @@ type Project struct { func (p *Project) validate() error { cfs, err := getFileInfo(p.Dir, p.Deps.ComposeFile) if err != nil { - return fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) + return err } p.Deps.ComposeFileInfo = cfs if p.Deps.EnvFile != "" { efs, err := getFileInfo(p.Dir, p.Deps.EnvFile) if err != nil { - return fmt.Errorf("%w, %v", ErrInvalidEnvFile, err) + return err } p.Deps.EnvFileInfo = efs } 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 { - return fmt.Errorf("%w, %v", ErrInvalidNginxFile, err) + return err } p.Deps.NginxFileInfo = nfs @@ -96,18 +92,15 @@ func ProjectFromDir(dir string) (Project, error) { content, err := os.ReadFile(filepath.Join(dir, ConfFile)) if err != nil { - log.Err(err).Str("dir", dir).Str("conf", ConfFile).Msg("unable to read conf file") - return p, err + return p, fmt.Errorf("%w, unable to read conf file=%s, err=%v", ErrProjectConfFile, ConfFile, err) } 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, err + return p, fmt.Errorf("%w, unable to parse conf file=%s, err=%v", ErrProjectConfFile, ConfFile, err) } if err := p.validate(); err != nil { - log.Err(err).Str("dir", dir).Msg("unable to validate project") - return p, err + return p, fmt.Errorf("%w, unable to validate project, name=%s, dir=%s, err=%v", ErrProjectConfFile, p.Name, p.Dir, err) } return p, nil