Go for system administrators. Practical examples. Part 0

Hello, my name is Vitaliy and I'm a monkeypractitioner - for me it is better to see and copy once than read abstract manuals a hundred times. For a long time I was a regular system administrator - I wrote scripts on CMD / BAT, and even on sh (using busybox for Windows). But once I began to miss the usual shell, and I decided to write my own RPC server for myself, but so that it worked with a minimum of system components, was understandable, multi-threaded and contained a minimum of lines of code. I dismissed Java and other OOP, because for professionals, it’s too abstract, and I need to put the environment for execution on the target computer, and I, as the administrator, update it. I looked at perl for a long time, but I'm afraid of dynamic typing. In the article I will tell you how to a person who is new to programming to solve some of the problems of system administration using! Go .

I assume that you have mastered "Quick Start - Go Programmable under Windows - Setting Environment" , have experience writing simple scripts. And I lied. The target machine may require Microsoft Visual C ++ .

First, we’ll try to turn a simple script into a Go app. For example, take test.bat :

@echo off
set URL1="ftp://user:pass@88.88.88.88/test.zip"
set URL2="ftp://user1:pass1@99.99.99.99/exchange/test.zip"
set SAVE_PATH=".\test\test.zip"
echo Первый источник
curl %URL1% -o %SAVE_PATH%
if %errorlevel% NEQ 0 (
echo  Загрузка из первого источника закончилась с ошибкой: %errorlevel%
curl %URL2% -o %SAVE_PATH%
)

A moment of love for Microsoft. If I want to check whether the file was downloaded from the second source, then I must use GOTO , because inside IF and FOR % errorlevel% and % time% remain the same as before calling IF and FOR .

Our Go script will look something like this:

package main
import (
	"fmt"
	"os"
	"os/exec"
)
func main() {
	url1 := "ftp://user:pass@88.88.88.88/test.zip"
	url2 := "ftp://user1:pass1@99.99.99.99/exchange/test.zip"
	out_file := ".\\test\\test.zip"     //В Go бэкслэш используется для экранирования, так что в пути windows его придется удваивать.
	err := start_curl(url1, out_file)
	if err != nil {
		fmt.Printf("Загрузка из первого источника закончилась с ошибкой: %v\r\n", err)
		err = start_curl(url2, out_file)
		if err != nil {
			fmt.Printf("Загрузка из второго источника закончилась с ошибкой: %v\r\n", err)
			os.Exit(1)
		}
	}
	fmt.Printf("Загрузка успешно завершена\r\n")
}
func start_curl(url string, out_file string) error {
	cmd := exec.Command("curl", url, "-o", out_file)
	err := cmd.Run()
	return err
}

We started the launch, but for debugging it would be nice to see what curl produces in stderr / stdout. By the way, curl displays everything in stderr:

func start_curl(url string, out_file string) error {
	cmd := exec.Command("curl", url, "-o", out_file)
	//cmd.Stdout = os.Stdout     //Закомментировать строку можно можно двумя слэшами.
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	return err
}

On error, the start_curl () function returns something like “exit code 7”. And we would like to receive a return code in the form of a number. We can cut the string “exit_code” and convert the string “7” to number7. To do this, you will have to import the strings and strconv packages. But there is a simpler, and less clear way:

package main
import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
)
func main() {
	url1 := "ftp://user:pass@88.88.88.88/test.zip"
	url2 := "ftp://user1:pass1@99.99.99.99/exchange/test.zip"
	out_file := ".\\test\\test.zip"
	int_err := start_curl(url1, out_file)
	if int_err != 0 {
		fmt.Printf("Загрузка из первого источника закончилась с ошибкой: %d\r\n", int_err)
		int_err = start_curl(url2, out_file)
		if int_err != 0 {
			fmt.Printf("Загрузка из второго источника закончилась с ошибкой: %d\r\n", int_err)
			os.Exit(int_err)
		}
	}
	fmt.Printf("Загрузка успешно завершена\r\n")
}
func start_curl(url string, out_file string) int {
	var exit_code int
	cmd := exec.Command("curl", url, "-o", out_file)
	//cmd.Stdout = os.Stdout
	//cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		exit_code = int(err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitCode)
	} else {
		exit_code = 0
	}
	return exit_code
}

That's all for today. The compiler will collect the finished exe file for us. To be afraid of a large size (several megabytes) is not necessary. The minimal runtime and all necessary packages will be collected in this file. The Go app’s memory consumption is half that of perl or python (unless of course we’re talking about small applications). If someone is interested in the article, indicate in the comments which of the topics I would like to consider:

  • work with text (parsing stdout, encodings)
  • files (information, search, logging)
  • work with winapi (connecting dll, calling functions, processing responses)
  • work with adodb (how to read data from the MSAccess database)
  • sending e-mail using Go
  • simple rpc server

Also popular now: