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 }