Hi,
Just as a intermediate post as i wanted to play a little bit with golang, let me show you what i managed to put together in some days. I created a virtual machine on which i installed docker and grabbed a sftp image. You can try first two from Docker Hub, it should work.
So i pulled this image and initiated two containers as shown below:
eaf3b93798b5 asavartzeth/sftp "/entrypoint.sh /u..." 21 hours ago Up About a minute 0.0.0.0:2225->22/tcp server4 ec7d7e1d029f asavartzeth/sftp "/entrypoint.sh /u..." 21 hours ago Up About a minute 0.0.0.0:2224->22/tcp server3
The command to do this looks like:
docker run --name server3 -v /home/sorin/sftp1:/chroot/sorin:rw -e SFTP_USER=sorin -e SFTP_PASS=pass -p 2224:22 -d asavartzeth/sftp docker run --name server4 -v /home/sorin/sftp2:/chroot/sorin:rw -e SFTP_USER=sorin -e SFTP_PASS=pass -p 2225:22 -d asavartzeth/sftp
Main info to know about these containers is that they should be accessible by user sorin and the path were the external directories are mapped is on /chroot/sorin.
You can manually test the connection by using a simple command like:
sftp -P 2224 sorin@localhost
If you are using the container ip address i observed that you will use the default 22 port to connect to them. Not really clear why but this is not about that.
Once the servers are up and running you can test the differences between the structure using following code:
package main
import (
"fmt"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
type ServerFiles struct {
Name string
files []string
}
func main() {
server1client := ConnectSftp("localhost:2224", "sorin", "pass")
server1files := ReadPath(server1client)
server1struct := BuildStruct("172.17.0.2", server1files)
server2client := ConnectSftp("localhost:2225", "sorin", "pass")
server2files := ReadPath(server2client)
server2struct := BuildStruct("172.17.0.3", server2files)
diffilesstruct := CompareStruct(server1struct, server2struct)
for _, values := range diffilestruct.files {
fmt.Printf("%s %s\n", diffilesstruct.Name, values)
}
CloseConnection(server1client)
CloseConnection(server2client)
}
func CheckError(err error) {
if err != nil {
panic(err)
}
}
func ConnectSftp(address string, user string, password string) *sftp.Client {
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
conn, err := ssh.Dial("tcp", address, config)
CheckError(err)
client, err := sftp.NewClient(conn)
CheckError(err)
return client
}
func ReadPath(client *sftp.Client) []string {
var paths []string
w := client.Walk("/")
for w.Step() {
if w.Err() != nil {
continue
}
paths = append(paths, w.Path())
}
return paths
}
func BuildStruct(address string, files []string) *ServerFiles {
server := new(ServerFiles)
server.Name = address
server.files = files
return server
}
func CompareStruct(struct1 *ServerFiles, struct2 *ServerFiles) *ServerFiles {
diff := difference(struct1.files, struct2.files)
diffstruct := new(ServerFiles)
for _, value := range diff {
for _, valueP := range struct1.files {
if valueP == value {
diffstruct.Name = struct1.Name
diffstruct.files = append(diffstruct.files, valueP)
}
}
for _, valueQ := range struct2.files {
if valueQ == value {
diffstruct.Name = struct2.Name
diffstruct.files = append(diffstruct.files, valueQ)
}
}
}
return diffstruct
}
func difference(slice1 []string, slice2 []string) []string {
var diff []string
// Loop two times, first to find slice1 strings not in slice2,
// second loop to find slice2 strings not in slice1
for i := 0; i < 2; i++ {
for _, s1 := range slice1 {
found := false
for _, s2 := range slice2 {
if s1 == s2 {
found = true
break
}
}
// String not found. We add it to return slice
if !found {
diff = append(diff, s1)
}
}
// Swap the slices, only if it was the first loop
if i == 0 {
slice1, slice2 = slice2, slice1
}
}
return diff
}
func CloseConnection(client *sftp.Client) {
client.Close()
}
This actually connects to each server, reads the hole filepath and puts it on a structure. After this is done for both servers, there is a method that compares only the slice part of the struct and returns the differences. On this differences there is another structure constructed with only the differences.
It is true that i took the differences func from stackoverflow, and it's far from good code, but i am working on it, this is the first draft, i will post different versions as it gets better.
The output if there are differences will look like this:
172.17.0.2 /sorin/subdirectory 172.17.0.2 /sorin/subdirectory/subtest.file 172.17.0.2 /sorin/test.file 172.17.0.3 /sorin/test2
If there are no differences that it will just exit.
Working on improving my golang experience. Keep you posted.
Cheers!