authelia/cmd/authelia-scripts/cmd_bootstrap.go
Clement Michaud a991379a74 Declare suites as Go structs and bootstrap e2e test framework in Go.
Some tests are not fully rewritten in Go, a typescript wrapper is called
instead until we remove the remaining TS tests and dependencies.

Also, dockerize every components (mainly Authelia backend, frontend and kind)
so that the project does not interfere with user host anymore (open ports for instance).
The only remaining intrusive change is the one done during bootstrap to add entries in /etc/hosts.
It will soon be avoided using authelia.com domain that I own.
2019-11-15 20:23:06 +01:00

225 lines
5.5 KiB
Go

package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
"github.com/clems4ever/authelia/utils"
"github.com/spf13/cobra"
)
// HostEntry represents an entry in /etc/hosts
type HostEntry struct {
Domain string
IP string
}
var hostEntries = []HostEntry{
// For common tests
HostEntry{Domain: "login.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "admin.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "singlefactor.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "dev.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "home.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "mx1.mail.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "mx2.mail.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "public.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "secure.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "mail.example.com", IP: "192.168.240.100"},
HostEntry{Domain: "duo.example.com", IP: "192.168.240.100"},
// For Traefik suite
HostEntry{Domain: "traefik.example.com", IP: "192.168.240.100"},
// For testing network ACLs
HostEntry{Domain: "proxy-client1.example.com", IP: "192.168.240.201"},
HostEntry{Domain: "proxy-client2.example.com", IP: "192.168.240.202"},
HostEntry{Domain: "proxy-client3.example.com", IP: "192.168.240.203"},
}
func runCommand(cmd string, args ...string) {
command := utils.CommandWithStdout(cmd, args...)
err := command.Run()
if err != nil {
panic(err)
}
}
func installNpmPackages() {
runCommand("npm", "ci")
}
func checkCommandExist(cmd string) {
fmt.Print("Checking if '" + cmd + "' command is installed...")
command := exec.Command("bash", "-c", "command -v "+cmd)
err := command.Run()
if err != nil {
log.Fatal("[ERROR] You must install " + cmd + " on your machine.")
}
fmt.Println(" OK")
}
func installClientNpmPackages() {
command := utils.CommandWithStdout("npm", "ci")
command.Dir = "client"
err := command.Run()
if err != nil {
panic(err)
}
}
func createTemporaryDirectory() {
err := os.MkdirAll("/tmp/authelia", 0755)
if err != nil {
panic(err)
}
}
func bootstrapPrintln(args ...interface{}) {
a := make([]interface{}, 0)
a = append(a, "[BOOTSTRAP]")
a = append(a, args...)
fmt.Println(a...)
}
func shell(cmd string) {
runCommand("bash", "-c", cmd)
}
func buildHelperDockerImages() {
shell("docker build -t authelia-example-backend example/compose/nginx/backend")
shell("docker build -t authelia-duo-api example/compose/duo-api")
shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml build")
shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.backend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)")
shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.frontend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)")
}
func prepareHostsFile() {
contentBytes, err := readHostsFile()
if err != nil {
panic(err)
}
lines := strings.Split(string(contentBytes), "\n")
toBeAddedLine := make([]string, 0)
modified := false
for _, entry := range hostEntries {
domainInHostFile := false
for i, line := range lines {
domainFound := strings.Contains(line, entry.Domain)
ipFound := strings.Contains(line, entry.IP)
if domainFound {
domainInHostFile = true
// The IP is not up to date.
if ipFound {
break
} else {
lines[i] = entry.IP + " " + entry.Domain
modified = true
break
}
}
}
if !domainInHostFile {
toBeAddedLine = append(toBeAddedLine, entry.IP+" "+entry.Domain)
}
}
if len(toBeAddedLine) > 0 {
lines = append(lines, toBeAddedLine...)
modified = true
}
err = ioutil.WriteFile("/tmp/authelia/hosts", []byte(strings.Join(lines, "\n")), 0644)
if err != nil {
panic(err)
}
if modified {
bootstrapPrintln("/etc/hosts needs to be updated")
shell("/usr/bin/sudo mv /tmp/authelia/hosts /etc/hosts")
}
}
// ReadHostsFile reads the hosts file.
func readHostsFile() ([]byte, error) {
bs, err := ioutil.ReadFile("/etc/hosts")
if err != nil {
return nil, err
}
return bs, nil
}
func readVersion(cmd string, args ...string) {
command := exec.Command(cmd, args...)
b, err := command.Output()
if err != nil {
panic(err)
}
fmt.Print(cmd + " => " + string(b))
}
func readVersions() {
readVersion("go", "version")
readVersion("node", "--version")
readVersion("docker", "--version")
readVersion("docker-compose", "--version")
}
// Bootstrap bootstrap authelia dev environment
func Bootstrap(cobraCmd *cobra.Command, args []string) {
bootstrapPrintln("Checking command installation...")
checkCommandExist("node")
checkCommandExist("docker")
checkCommandExist("docker-compose")
bootstrapPrintln("Getting versions of tools")
readVersions()
bootstrapPrintln("Checking if GOPATH is set")
goPathFound := false
for _, v := range os.Environ() {
if strings.HasPrefix(v, "GOPATH=") {
goPathFound = true
break
}
}
if !goPathFound {
log.Fatal("GOPATH is not set")
}
bootstrapPrintln("Installing NPM packages for development...")
installNpmPackages()
bootstrapPrintln("Building development Docker images...")
buildHelperDockerImages()
createTemporaryDirectory()
bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...")
prepareHostsFile()
bootstrapPrintln("Run 'authelia-scripts suites setup Standalone' to start Authelia and visit https://home.example.com:8080.")
bootstrapPrintln("More details at https://github.com/clems4ever/authelia/blob/master/docs/getting-started.md")
}