Upclapi/run.go

488 lines
14 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"strconv"
"strings"
"github.com/joho/godotenv"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/client"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
)
func defaultSSHAccess(host string) SSHAccess {
sshaccess := SSHAccess{Host: host}
var err error
sshaccess.Port, err = strconv.Atoi(os.Getenv("SSH_PORT"))
if err != nil {
sshaccess.Port = 22
}
sshaccess.User = os.Getenv("SSH_USER")
if len(sshaccess.User) == 0 {
sshaccess.User = DFLT_SSH_USER
}
sshaccess.Password = ""
sshaccess.KeyPath = ""
return sshaccess
}
func cmdexec(command string, args []string) (string, error) {
cmd := &exec.Cmd {
Path: command,
Args: args,
// Stdout: os.Stdout,
// Stderr: os.Stdout,
}
if out, err := cmd.Output(); err != nil {
fmt.Fprintf(os.Stderr, "%s error: %#v\n",command,err)
return "",err
} else {
return string(out), nil
}
// cmd.Start();
// cmd.Wait()
}
func run() int {
runFlags, err := ParseFlags()
if err != nil {
log.Fatal(err)
}
username := os.Getenv("UPCLOUD_USERNAME")
password := os.Getenv("UPCLOUD_PASSWORD")
k := os.Getenv("KUPCLAPI")
if len(username) == 0 && len(k) == 0 {
err = godotenv.Load("/etc/upcloud/.env")
if err != nil {
err = godotenv.Load("/usr/local/etc/upcloud/.env")
}
}
if runFlags.encdr == TECODER.cmd || runFlags.encdr == TECODER.abbrv {
k := os.Getenv("KUPCLAPI")
if len(k) > 0 {
kdata := strings.Split(k," ")
out, err := exec.Command(TECODER.cmd,TECODER.decoder,kdata[0]).Output()
if err != nil {
fmt.Fprintf(os.Stderr, "Auth info: Username not found in ecoded: %#v\n ",err)
return 1
}
username = strings.TrimSuffix(string(out),"\n")
out, err = exec.Command(TECODER.cmd,TECODER.decoder,kdata[1]).Output()
if err != nil {
fmt.Fprintf(os.Stderr, "Auth info: Password not found in ecoded: %#v\n",err)
return 1
}
password = strings.TrimSuffix(string(out),"\n")
}
} else {
username = os.Getenv("UPCLOUD_USERNAME")
password = os.Getenv("UPCLOUD_PASSWORD")
}
if len(username) == 0 {
var upcloud_env map[string]string
upcloud_env, err = godotenv.Read(os.Getenv("HOME")+ "/.config/upctl.yaml")
if len(upcloud_env["username"]) != 0 {
username = upcloud_env["username"]
password = upcloud_env["password"]
}
}
if len(username) == 0 {
fmt.Fprintln(os.Stderr, "Auth info: Username must be specified")
return 1
}
if len(password) == 0 {
fmt.Fprintln(os.Stderr, "Auth info: Password must be specified")
return 2
}
c := client.New(username, password)
s := service.New(c)
switch runFlags.command {
case "infoserver":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"info",runFlags,datacfg)
case "inventory":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"inventory",runFlags,datacfg)
case "pubhosts":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"pubhosts",runFlags,datacfg)
case "prvhosts":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"prvhosts",runFlags,datacfg)
case "deleteserver":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"delete",runFlags,datacfg)
case "startserver":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"start",runFlags,datacfg)
case "restartserver":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"restart",runFlags,datacfg)
case "stopserver":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"stop",runFlags,datacfg)
case "createserver":
datacfg,err := loadDataConfig(runFlags)
if err != nil {
return 99
}
sshkeys := loadLoginUserKeys(runFlags)
if err := createServer(s,datacfg,sshkeys); err != nil {
return 3
}
case "modifyserver":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
if runFlags.target == "" {
fmt.Fprintln(os.Stderr, "Unable to run modifyserver command: ", runFlags.command)
return 99
}
onServers(s,"modifyserver",runFlags,datacfg)
case "fixstorage":
datacfg,err := loadDataConfig(runFlags)
if err != nil {
return 99
}
if runFlags.target == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"fixstorage",runFlags,datacfg)
case "modifystorage":
storecfg,err := loadStoreConfig(runFlags.dataCfgPath)
if err != nil {
return 99
}
if err := modifyStorage(s,storecfg); err != nil {
return 3
}
case "deletestorage":
hasID(runFlags.id)
if err := deleteStorage(s, runFlags.id); err != nil {
return 2
}
case "liststorages":
datacfg,_ := loadDataConfig(runFlags)
if err := listStorages(s, datacfg, runFlags.target); err != nil {
return 2
}
case "createstorageimage":
hasID(runFlags.id)
if runFlags.target == "" {
fmt.Fprintln(os.Stderr, "Unable to run createStorageImage no title: ", runFlags.target)
return 99
}
if err := createStorageImage(s, runFlags.id,runFlags.target); err != nil {
return 2
}
case "runssh":
if len(runFlags.runCmd) == 0 {
fmt.Fprintln(os.Stderr, "Unable to run ssh command: ", runFlags.command)
return 99
}
if len(runFlags.id) == 0 {
datacfg,err := loadDataConfig(runFlags)
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to run ssh command: ", runFlags.command)
return 99
}
onServers(s,"runssh",runFlags,datacfg)
} else {
sshAccess := defaultSSHAccess(runFlags.id)
output, err := runSSH(runFlags, sshAccess, runFlags.runCmd)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(output))
}
case "runcmd":
if len(runFlags.runCmd) == 0 {
fmt.Fprintln(os.Stderr, "Unable to run command: ", runFlags.command)
return 99
}
datacfg,err := loadDataConfig(runFlags)
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to run command: ", runFlags.command)
return 99
}
onServers(s,"runcmd",runFlags,datacfg)
case "infofloatip":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
ipinfo, err := getFloatIP(s,runFlags.id,datacfg)
if err != nil {
fmt.Fprintf(os.Stderr, "error getting info %s: %#v\n", datacfg.FloatIP, err)
return 99
}
if runFlags.out == "attached" {
ipData := strings.Split(ipinfo.PTRRecord,".")
ip := strings.Join(ipData[0:4],".")
fmt.Fprintf(os.Stdout, "%s\n",ip)
} else {
enc := json.NewEncoder(os.Stdout)
enc.Encode(ipinfo)
}
// fmt.Fprintf(os.Stderr, "%s Located in %s %s Server: %s) \n", datacfg.FloatIP, ipinfo.PTRRecord, ipinfo.MAC, ipinfo.ServerUUID)
case "movefloatip":
datacfg := &DataConfig{}
id := runFlags.id
var data []string
if id == "" {
id = os.Getenv("UPCLOUD_MOVEFLOATIP")
}
if id == "" {
datacfg,err = loadDataConfig(runFlags)
if datacfg.FloatIP == "" {
fmt.Fprintln(os.Stderr, "Float IP not found in: ", runFlags.dataCfgPath)
return 99
}
} else {
data = strings.Split(id, SEPARATOR)
}
moveFloatIP(s,data,datacfg)
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to move Float IP: ", datacfg.FloatIP)
return 99
}
case "modifyip":
datacfg,err := loadDataConfig(runFlags)
if err != nil {
return 99
}
onServers(s,"modifyip",runFlags,datacfg)
case "infotags":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"infotags",runFlags,datacfg)
case "addtags":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"addtags",runFlags,datacfg)
case "deletetags":
datacfg := &DataConfig{}
if runFlags.id == "" {
datacfg,err = loadDataConfig(runFlags)
}
onServers(s,"deletetags",runFlags,datacfg)
default:
fmt.Fprintln(os.Stderr, "Unknown command: ", runFlags.command)
return 99
}
return 0
}
func onServers(s *service.Service, tsksrvc string, runFlags RunFlags, datacfg *DataConfig) error {
uuid := runFlags.id
runCommand := runFlags.runCmd
servers, err := s.GetServers()
if err != nil || len(servers.Servers) == 0 {
fmt.Fprintf(os.Stderr, "Unable to get servers: %#v\n", err)
return err
}
inventory := map[string][]string{}
sshAccess := SSHAccess{}
var target ServerConfig
for _, server := range servers.Servers {
target_srvr := ""
role_srvr := ""
if uuid != "" {
if server.UUID == uuid || server.Hostname == uuid {
target_srvr = uuid
for _,srv := range datacfg.Servers {
if srv.Hostname == server.Hostname {
target = srv
role_srvr = srv.Cluster.Role
sshAccess = srv.SSHAccess
break;
}
}
}
} else {
if datacfg.HostPrefix != "" && !strings.Contains(server.Hostname,datacfg.HostPrefix) {
continue
}
for _,srv := range datacfg.Servers {
if srv.Hostname == server.Hostname || srv.UUID == server.UUID {
target_srvr = server.UUID
target = srv
role_srvr = srv.Cluster.Role
sshAccess = srv.SSHAccess
break;
}
}
}
if target_srvr == "" {
continue
}
switch tsksrvc {
case "delete":
err := deleteOneServer(s, server, runFlags.target)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to delete server %#v: %#v\n", server.UUID, err)
return err
}
case "stop":
err := stopOneServer(s, server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop server %#v: %#v\n", server.UUID, err)
}
case "start":
err := startOneServer(s, server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to start server %#v: %#v\n", server.UUID, err)
}
case "restart":
err := restarOneServer(s, server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to restart server %#v: %#v\n", server.UUID, err)
}
case "info":
dataPrices,err_prices := getDataPrices()
if err_prices != nil {
fmt.Fprintf(os.Stderr, "Unable to load prices for server %#v: %#v\n", server.UUID, err_prices)
}
err := detailsOneServer(s, server, *dataPrices)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get details for server %#v: %#v\n", server.UUID, err)
}
case "inventory":
info, err := inventoryOneServer(s, datacfg.FloatIP, server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to make inventory from server %#v: %#v\n", server.UUID, err)
} else {
inventory[role_srvr] = append(inventory[role_srvr],info)
}
case "pubhosts":
info, err := hostsOneServer(s,"pub", datacfg.FloatIP, server, target)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to make hosts list from server %#v: %#v\n", server.UUID, err)
} else {
fmt.Printf("%s",info)
}
case "prvhosts":
info, err := hostsOneServer(s,"prv", datacfg.FloatIP, server, target)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to make hosts list from server %#v: %#v\n", server.UUID, err)
} else {
fmt.Printf("%s",info)
}
case "modifyip":
nettype := upcloud.NetworkTypePrivate
err := modifyIPOneServer(s, server, nettype, target)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to modify IP server %#v: %#v\n", server.UUID, err)
}
case "infotags":
tags, err := infoTagsFromId(s, server.UUID)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get tags from server %#v: %#v\n", server.UUID, err)
} else {
enc := json.NewEncoder(os.Stdout)
enc.Encode(tags)
}
case "addtags":
if uuid != "" {
fmt.Fprintf(os.Stderr, "No tags found, for server %#v: %#v\n", server.UUID, err)
} else {
err := addTagsToId(s, server.UUID, target.Tags)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to add tags to server %#v: %#v\n", server.UUID, err)
}
}
case "deletetags":
if uuid != "" {
fmt.Fprintf(os.Stderr, "No tags found, for server %#v: %#v\n", server.UUID, err)
} else {
err := deleteTagsFromId(s, server.UUID, target.Tags)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to delete tags from server %#v: %#v\n", server.UUID, err)
}
}
case "fixstorage":
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: server.UUID,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get details from server %#v: %#v\n", server.UUID, err)
} else {
fixStorageDevices(s, target, info, runFlags.target)
}
case "modifyserver":
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: server.UUID,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get details from server %#v: %#v\n", server.UUID, err)
} else {
modifyServer(s, target, info, runFlags.target)
}
case "runssh":
if sshAccess.Host != "" {
output, err := runSSH(runFlags, sshAccess, runCommand)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(output))
} else {
fmt.Fprintf(os.Stderr, "No Host to run ssh on %s \n", server.UUID)
}
case "runcmd":
args := strings.Split(runCommand, " ")
output, err := cmdexec(args[0],args[1:])
if err != nil {
log.Fatal(err)
fmt.Fprintf(os.Stderr, "No Host to run on %s \n", server.UUID)
}
fmt.Println(string(output))
}
}
if tsksrvc == "inventory" {
for key,items := range inventory {
fmt.Printf("[%s]\n",key)
for _,item := range items {
fmt.Printf("%s\n",item)
}
}
}
return nil
}