Skip to content

Commit

Permalink
Merge pull request #12 from APoniatowski/Dev
Browse files Browse the repository at this point in the history
Dev -> v1.0.0
  • Loading branch information
APoniatowski authored Jan 20, 2020
2 parents 001aa09 + 2389e35 commit 97bdfb1
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 62 deletions.
14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GoSSH - Open Source Go Infrastucture Automation tool

![](https://github.com/Aponiatowski/GoSSH/workflows/GoSSH/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/APoniatowski/GoSSH)](https://goreportcard.com/report/github.com/APoniatowski/GoSSH)
![](https://github.com/Aponiatowski/GoSSH/workflows/GoSSH/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/APoniatowski/GoSSH)](https://goreportcard.com/report/github.com/APoniatowski/GoSSH) [![codebeat badge](https://codebeat.co/badges/e53dab58-a0df-4699-a4d6-cfe67fbd9b81)](https://codebeat.co/projects/jackfan.us.kg-aponiatowski-gossh-master)

## Project update:
It is currently in an usable state, and can be used to execute commands in varied ways and performs well. :+1:
Expand All @@ -10,12 +10,11 @@ written to a log file for review. If running a command was successful, why would
The logs are also rotated by date, to avoid multiple logs, if time was added to the file name.
The logging will be enchanced even further, as the project continues.

Currently working on adding the ability to run commands as sudo, and also add some security for connecting to known hosts (see issue board for clarification ( [Issue #7](https://github.com/APoniatowski/GoSSH/issues/7) )
Sudo commands are possible now. Just make sure you add the username's password to the password field, and it will be used.

I have also removed some possible features, that I was planning on implementing. [^1]

But rather chose to implement another feature for this release. [^2]
The known_hosts file is causing some issues (issue open for it). Trying to resolve that, before release

Also redoing the CLI, as I would like it to be more seamless and intuitive. I will also be adding bash completion later, once I finish the new CLI.

* Windows (laptop):
##### 22 production servers (across 8 different countries):
Expand Down Expand Up @@ -55,8 +54,3 @@ Options:

## Please feel free to test/use this and leave issues and comments in the issues tab.
## I will be actively working on this for the foreseeable future


[^1]: Creating a client side agent, this might possibly be added for v2.0.0 release. Not guaranteed though.

[^2]: Running a bash script with little effort. Makes things simpler, than trying to cat | gossh all, or gosh all $(cat my-script.sh), etc.
34 changes: 34 additions & 0 deletions clioptions/clioptions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package clioptions

import (
"bufio"
"os"
"strconv"
"strings"
)

// GeneralCommandParse CLI parser for general commands to linux servers
func GeneralCommandParse(cmd []string) string {
command := strconv.Quote(strings.Join(cmd, " "))
command = "sh -c " + command + " 2>&1"
return command
}

//BashScriptParse bash script parser, to pass write the script on the server, run it and remove it.
// It also accepts args for the script. Dependency scripts will not work, as they are considered a separate script
func BashScriptParse(cmd string, cmdargs []string) string {
scriptargs := strings.Join(cmdargs, " ")
script, _ := os.Open(cmd)
defer script.Close()
scanner := bufio.NewScanner(script)
scanner.Split(bufio.ScanLines)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
lines = append(lines, "\n")
}
parsedcmd := strconv.Quote(strings.Join(lines, ""))
parsedcmd = strings.Replace(parsedcmd, `$`, `\$`, -1)
parsedlines := "set +H;echo -e " + parsedcmd + " > /tmp/gossh-script.sh;bash /tmp/gossh-script.sh " + scriptargs + ";rm /tmp/gossh-script.sh;set -H"
return parsedlines
}
18 changes: 7 additions & 11 deletions config/config.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
ServerGroup1:
Server11:
FQDN: hostname11.whatever.com
Username: user11
Password: password11
Key_Path: /path/to/key
Port: 22
Need_Sudo: false
ServerGroup1: #group name, spacing does not matter
Server11: #server name, spacing does not matter
FQDN: hostname11.whatever.com #
Username: user11 ##
Password: password11 ### FQDN, is needed. Username defaults to root,
Key_Path: /path/to/key ## password or key needed, ports default to 22
Port: 22 #
Server12:
FQDN: hostname12.whatever.com
Username: user12
Password: password12
Key_Path: /path/to/key
Port: 223
Need_Sudo: true
ServerGroup2:
Server21:
FQDN: hostname21.whatever.com
Username: user21
Password: password21
Key_Path: /path/to/key
Port: 2233
Need_Sudo: false
Server22:
FQDN: hostname22.whatever.com
Username: user22
Password: password22
Key_Path: /path/to/key
Port:
Need_Sudo: true
8 changes: 4 additions & 4 deletions loggerlib/loggerlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// OutputLogger All INFO/WARNING messages will be written in this function
func OutputLogger(servername string, output []byte) {
func OutputLogger(servername string, option string, output []byte) {
currentDate := time.Now()
dateFormatted := currentDate.Format("2006-01-02")
path, _ := filepath.Abs("./logs/output/")
Expand All @@ -19,15 +19,15 @@ func OutputLogger(servername string, output []byte) {
log.Println(err)
}
defer okFile.Close()
logger := log.New(okFile, "[INFO: Succeeded] ", log.LstdFlags)
logger := log.New(okFile, option, log.LstdFlags)
logger.Print(servername + ": " + string(output))
} else {
log.Println(err)
}
}

// ErrorLogger All ERROR/FATAL messages will be written in this function
func ErrorLogger(servername string, output []byte) {
func ErrorLogger(servername string, option string, output []byte) {
currentDate := time.Now()
dateFormatted := currentDate.Format("2006-01-02")
path, _ := filepath.Abs("./logs/errors/")
Expand All @@ -38,7 +38,7 @@ func ErrorLogger(servername string, output []byte) {
log.Println(err)
}
defer errFile.Close()
logger := log.New(errFile, "[INFO: Failed] ", log.LstdFlags)
logger := log.New(errFile, option, log.LstdFlags)
logger.Print(servername + ": " + string(output))
} else {
log.Println(err)
Expand Down
160 changes: 128 additions & 32 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,146 @@
package main

import (
"fmt"
"log"
"os"
"strconv"
"strings"

"github.com/APoniatowski/GoSSH/clioptions"
"github.com/APoniatowski/GoSSH/sshlib"
"github.com/APoniatowski/GoSSH/yamlparser"
"github.com/urfave/cli"
)

// Error checking function
func generalError(e error) {
if e != nil {
log.Fatal(e)
}
}

// Main function to carry out operations
func main() {
var cmd []string
if len(os.Args) > 2 {
cmd = os.Args[2:] // will change this to 3 later, when I see a need to expand on more arguments, eg. running only 1 group, or x amount of servers
} else {
fmt.Println("No command was specified, please specify a command.")
os.Exit(1)

app := cli.NewApp()
app.Name = "GoSSH"
app.Version = "1.0.0"
app.Usage = "Open Source Go Infrastucture Automation Tool"
app.UsageText = "GoSSH [global options] command [subcommand] [script or arguments...]"
app.EnableBashCompletion = true
app.Commands = []cli.Command{
{
Name: "sequential",
Aliases: []string{"s"},
Usage: "Run the command sequentially on all servers in your config file",
Action: func(c *cli.Context) error {
yamlparser.Rollcall()
cmd = os.Args[2:]
command := clioptions.GeneralCommandParse(cmd)
sshlib.RunSequentially(&yamlparser.Config, &command)
return nil
},
Subcommands: []cli.Command{
{
Name: "run",
Usage: "Run a bash script on the defined servers",
Action: func(c *cli.Context) error {
yamlparser.Rollcall()
cmd := os.Args[3]
cmdargs := os.Args[4:]
command := clioptions.BashScriptParse(cmd, cmdargs)
sshlib.RunAllServers(&yamlparser.Config, &command)
return nil
},
},
//-----------------placeholder--------------------
// {
// Name: "remove",
// Usage: "remove an existing template",
// Action: func(c *cli.Context) error {
// fmt.Println("removed task template: ", c.Args().First())
// return nil
// },
// },
//-----------------placeholder--------------------
},
},
{
Name: "groups",
Aliases: []string{"g"},
Usage: "Run the command on all servers per group concurrently in your config file",
Action: func(c *cli.Context) error {
yamlparser.Rollcall()
cmd = os.Args[2:]
command := clioptions.GeneralCommandParse(cmd)
sshlib.RunGroups(&yamlparser.Config, &command)
return nil
},
Subcommands: []cli.Command{
{
Name: "run",
Usage: "Run a bash script on the defined servers",
Action: func(c *cli.Context) error {
yamlparser.Rollcall()
cmd := os.Args[3]
cmdargs := os.Args[4:]
command := clioptions.BashScriptParse(cmd, cmdargs)
sshlib.RunAllServers(&yamlparser.Config, &command)
return nil
},
},
//-----------------placeholder--------------------
// {
// Name: "remove",
// Usage: "remove an existing template",
// Action: func(c *cli.Context) error {
// fmt.Println("removed task template: ", c.Args().First())
// return nil
// },
// },
//-----------------placeholder--------------------
},
},
{
Name: "all",
Aliases: []string{"a"},
Usage: "Run the command on all servers concurrently in your config file",
Action: func(c *cli.Context) error {
yamlparser.Rollcall()
cmd = os.Args[2:]
command := clioptions.GeneralCommandParse(cmd)
sshlib.RunAllServers(&yamlparser.Config, &command)
return nil
},
Subcommands: []cli.Command{
{
Name: "run",
Usage: "Run a bash script on the defined servers",
Action: func(c *cli.Context) error {
yamlparser.Rollcall()
cmd := os.Args[3]
cmdargs := os.Args[4:]
command := clioptions.BashScriptParse(cmd, cmdargs)
sshlib.RunAllServers(&yamlparser.Config, &command)
return nil
},
},
//-----------------placeholder--------------------
// {
// Name: "remove",
// Usage: "remove an existing template",
// Action: func(c *cli.Context) error {
// fmt.Println("removed task template: ", c.Args().First())
// return nil
// },
// },
//-----------------placeholder--------------------
},
},
}

command := strconv.Quote(strings.Join(cmd, " "))
command = "sh -c " + command + " 2>&1"
yamlparser.Rollcall()
// app.Flags = []cli.Flag{
// cli.StringFlag{
// Name: "lang, l",
// Value: "english",
// Usage: "language for the greeting",
// },
// }

switch options := os.Args[1]; options {
case "seq":
sshlib.RunSequentially(&yamlparser.Config, &command)
case "groups":
sshlib.RunGroups(&yamlparser.Config, &command)
case "all":
sshlib.RunAllServers(&yamlparser.Config, &command)
default:
fmt.Println("Usage: gossh [option] [command]")
fmt.Println("Options:")
fmt.Println(" seq - Run the command sequentially on all servers in your config file")
fmt.Println(" groups - Run the command on all servers per group concurrently in your config file")
fmt.Println(" all - Run the command on all servers concurrently in your config file")
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}

}
8 changes: 4 additions & 4 deletions sshlib/sshlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ func executeCommand(servername string, cmd string, password string, connection *
_, err = session.Output(cmd)
if err != nil {
validator = "NOK\n"
loggerlib.ErrorLogger(servername, terminaloutput)
loggerlib.ErrorLogger(servername, "[INFO: Failed] ", terminaloutput)
} else {
validator = "OK\n"
loggerlib.OutputLogger(servername, terminaloutput)
loggerlib.OutputLogger(servername, "[INFO: Success] ", terminaloutput)
}
return validator
}
Expand Down Expand Up @@ -104,7 +104,7 @@ func connectAndRun(command *string, servername string, fqdn string, username str
ssh.KeyAlgoECDSA521,
ssh.KeyAlgoED25519,
},
Timeout: 5 * time.Second,
Timeout: 15 * time.Second,
}
connection, err := ssh.Dial("tcp", fqdn+":"+port, sshConfig)
loggerlib.GeneralError(err)
Expand Down Expand Up @@ -139,7 +139,7 @@ func connectAndRunSeq(command *string, servername string, fqdn string, username
ssh.KeyAlgoECDSA521,
ssh.KeyAlgoED25519,
},
Timeout: 5 * time.Second,
Timeout: 15 * time.Second,
}
connection, err := ssh.Dial("tcp", fqdn+":"+port, sshConfig)
loggerlib.GeneralError(err)
Expand Down
2 changes: 1 addition & 1 deletion yamlparser/yamlparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func Rollcall() {
fmt.Printf("\n")
fmt.Printf("Total groups of servers: %d\n", Grouptotal)
fmt.Printf("Total number of logical cores: %v\n", runtime.NumCPU())
fmt.Printf("=========================================\n")
fmt.Printf("======================================================\n")
}

// ParseServersList server list parser, parses it to a map of structs in main function
Expand Down

0 comments on commit 97bdfb1

Please sign in to comment.