diff --git a/internal/cmd/build/build.go b/internal/cmd/build/build.go index f1fbd51..b8ac8b7 100644 --- a/internal/cmd/build/build.go +++ b/internal/cmd/build/build.go @@ -124,7 +124,7 @@ func NewCommand() *cli.Command { // Destroy server // on close. if !argv.KeepServer { - defer destroyCurrentServer(&client.Server, hf.SHA256Sum) + defer destroyCurrentServer(client, hf.SHA256Sum) } vars, err := helpers.ReadVarsJsonFile(argv.VarsPath) @@ -388,11 +388,11 @@ func checkErrorStatus(state *statusT, err error) error { return err } -func destroyCurrentServer(sclient *hcloud.ServerClient, UniqueID string) { +func destroyCurrentServer(client *hcloud.Client, UniqueID string) { serverName := helpers.ServerNameFromSHA256(UniqueID) fmt.Println("Destroying ", serverName) - helpers.TryDeleteServer(sclient, serverName, 20, 5) + helpers.TryDeleteServer(client, serverName, 20, 5) } func statusServer(state *statusT) { diff --git a/internal/cmd/clean/clean.go b/internal/cmd/clean/clean.go index d7e509a..8d19549 100644 --- a/internal/cmd/clean/clean.go +++ b/internal/cmd/clean/clean.go @@ -54,7 +54,7 @@ func NewCommand() *cli.Command { } fmt.Println("Destroying all HAM Dead Servers.") - err = helpers.DestroyAllDeadServers(&client.Server) + err = helpers.DestroyAllDeadServers(client) if err != nil { return err } @@ -86,6 +86,7 @@ func destroyHamServers(client *hcloud.Client) error { continue } + serverName := server.Name fmt.Printf("Destroying... %s\n", server.Name) result, _, err := client.Server.DeleteWithResult( context.Background(), @@ -102,6 +103,12 @@ func destroyHamServers(client *hcloud.Client) error { if !ok { return errors.New(errMsg) } + + // Delete Volumes too + err = helpers.DeleteVolume(&client.Volume, serverName) + if err != nil { + return err + } } return nil diff --git a/internal/cmd/get/get.go b/internal/cmd/get/get.go index 2f309c6..a906a4a 100644 --- a/internal/cmd/get/get.go +++ b/internal/cmd/get/get.go @@ -288,7 +288,7 @@ Local Recipe: // whenver we see them. // This is highly unlikely that our ham leaves dead servers // but this is just a precaution. - err = helpers.DestroyAllDeadServers(&client.Server) + err = helpers.DestroyAllDeadServers(client) if err != nil { return err } @@ -340,7 +340,7 @@ Local Recipe: // This is a safety net. destroyServer := !argv.KeepServer - defer deferDeleteServer(&client.Server, &destroyServer, serverName) + defer deferDeleteServer(client, &destroyServer, serverName) // Hmm... My ISP and mostly a lot of dumb ISP's don't support IPv6 // and tunnel is a waste of time. Also IPv4 cost a little extra on @@ -408,7 +408,8 @@ Local Recipe: fmt.Printf(" %s Created Server\n", checkMark) } - err = doInitialize(ipAddr, config.SSHPrivateKey, varsFilePath, fileUploads, usedGit, gitUrl, gitBranch, dir, argv.TestingBinary) + volDevice := currentBuildServer.Volumes[0].LinuxDevice + err = doInitialize(ipAddr, config.SSHPrivateKey, volDevice, varsFilePath, fileUploads, usedGit, gitUrl, gitBranch, dir, argv.TestingBinary) if err != nil { return err } @@ -510,7 +511,12 @@ Local Recipe: tries = 0 defer os.Remove(varsFilePath) - err = doInitialize(ipAddr, config.SSHPrivateKey, varsFilePath, fileUploads, usedGit, gitUrl, gitBranch, dir, argv.TestingBinary) + volDevice := "" + if currentBuildServer != nil { + volDevice = currentBuildServer.Volumes[0].LinuxDevice + } + + err = doInitialize(ipAddr, config.SSHPrivateKey, volDevice, varsFilePath, fileUploads, usedGit, gitUrl, gitBranch, dir, argv.TestingBinary) if err != nil { return err } @@ -556,7 +562,7 @@ Local Recipe: "Cannot Get SSH Client (" + err.Error() + "), But Server is Kept and Still Running.") } - delErr := helpers.TryDeleteServer(&client.Server, serverName, 20, 5) + delErr := helpers.TryDeleteServer(client, serverName, 20, 5) if delErr != nil { banner.GetConnectFailBanner(serverName) return delErr @@ -581,7 +587,7 @@ Local Recipe: return errors.New("Malformed JSON from Build Server, But Server is Kept and Still Running.") } - delErr := helpers.TryDeleteServer(&client.Server, serverName, 20, 5) + delErr := helpers.TryDeleteServer(client, serverName, 20, 5) if delErr != nil { banner.GetMalformedJSONBanner(serverName) return delErr @@ -596,7 +602,7 @@ Local Recipe: return errors.New("Remote Build Failed, But Server is Kept and Still Running.") } - delErr := helpers.TryDeleteServer(&client.Server, serverName, 20, 5) + delErr := helpers.TryDeleteServer(client, serverName, 20, 5) if delErr != nil { banner.GetBuildFailedBanner(serverName) return delErr @@ -607,7 +613,7 @@ Local Recipe: } else { tries++ if tries >= 3 { - delErr := helpers.TryDeleteServer(&client.Server, serverName, 20, 5) + delErr := helpers.TryDeleteServer(client, serverName, 20, 5) if delErr != nil { return delErr } @@ -678,14 +684,15 @@ Local Recipe: // it can try to delete any created server. The state is checked if // we have to delete the server since it may not be desired by the // user. -func deferDeleteServer(sclient *hcloud.ServerClient, destroy *bool, serverName string) { +func deferDeleteServer(client *hcloud.Client, destroy *bool, serverName string) { if destroy != nil && *destroy { - helpers.TryDeleteServer(sclient, serverName, 5, 5) + helpers.TryDeleteServer(client, serverName, 5, 5) } } func doInitialize(ipAddr string, privateKey string, + volumeLinuxDevice string, varsFilePath string, fileUploads map[string]string, usedGit bool, @@ -927,6 +934,31 @@ func doInitialize(ipAddr string, _ = spinnerMsg.StopMessage() + if volumeLinuxDevice != "" { + spinnerMsg.ShowMessage("Mounting Volume... ") + + mountStatus, err := tryExec("mountpoint /ham-build") + if err != nil { + return err + } + + if strings.Contains(mountStatus, "not a mountpoint") { + _, err = tryExec(fmt.Sprintf("mkfs.ext4 %s", volumeLinuxDevice)) + if err != nil { + return err + } + + _, err = tryExec(fmt.Sprintf("mount -o discard,defaults %s /ham-build", volumeLinuxDevice)) + + if err != nil { + return err + } + + } + + _ = spinnerMsg.StopMessage() + } + _, err = tryExec("echo 'finished' > /tmp/ham.init.finished") if err != nil { return err diff --git a/internal/cmd/get/server_pricing.go b/internal/cmd/get/server_pricing.go index 51b7c18..7e20066 100644 --- a/internal/cmd/get/server_pricing.go +++ b/internal/cmd/get/server_pricing.go @@ -11,11 +11,12 @@ import ( ) func GrossServerPriceForServerWithHighestPerformance(client *hcloud.Client) (float64, *hcloud.ServerType, error) { - // CPX51 - // vCPU: 16 + // CCX33 + // vCPU: 8 // Memory: 32 GB - // Disk: 360 GB - return GrossServerPriceForServerType(client, "cpx51" /*"cx11"*/) + // Disk: 240 GB + // Additional Volume: 400 GB for Lineage Build + return GrossServerPriceForServerType(client, "ccx33" /*"cpx51"*/) } func GrossServerPriceForServerType(client *hcloud.Client, serverType string) (float64, *hcloud.ServerType, error) { @@ -34,51 +35,45 @@ func GrossServerPriceForServerType(client *hcloud.Client, serverType string) (fl } for _, server := range pricing.ServerTypes { - // CCX are Dedicated Servers which cost a lot - // of money, it's just better avoid them, maybe - // in the future we might have an option to use it - // for rich users xD. - if !strings.HasPrefix(strings.ToLower(server.ServerType.Name), "ccx") { - // fmt.Printf("Server Name: %s\n", server.ServerType.Name) - // fmt.Printf("Server Cores: %d\n", server.ServerType.Cores) - // fmt.Printf("Server Memory: %f\n", server.ServerType.Memory) - // fmt.Printf("Server Disk: %d\n", server.ServerType.Disk) + // fmt.Printf("Server Name: %s\n", server.ServerType.Name) + // fmt.Printf("Server Cores: %d\n", server.ServerType.Cores) + // fmt.Printf("Server Memory: %f\n", server.ServerType.Memory) + // fmt.Printf("Server Disk: %d\n", server.ServerType.Disk) - var hourlyPrice hcloud.Price - priceAvail := false - // fmt.Printf("Locations: ") - for _, entry := range server.Pricings { - // fmt.Printf("%s ", entry.Location.Name) - if strings.ToLower(entry.Location.Name) == TargetLocation { - hourlyPrice = entry.Hourly - priceAvail = true - break - } + var hourlyPrice hcloud.Price + priceAvail := false + // fmt.Printf("Locations: ") + for _, entry := range server.Pricings { + // fmt.Printf("%s ", entry.Location.Name) + if strings.ToLower(entry.Location.Name) == TargetLocation { + hourlyPrice = entry.Hourly + priceAvail = true + break } - // fmt.Printf("\n\n") + } + // fmt.Printf("\n\n") - if !priceAvail { - continue - } + if !priceAvail { + continue + } - amount, err := strconv.ParseFloat(hourlyPrice.Gross, 64) - if err != nil { - return 0.0, nil, errors.New("Invalid Price Given") - } + amount, err := strconv.ParseFloat(hourlyPrice.Gross, 64) + if err != nil { + return 0.0, nil, errors.New("Invalid Price Given") + } - if len(serverType) != 0 { - if server.ServerType.Name == serverType { - return amount, server.ServerType, nil - } - } else { - // For Some Reason, These all returns Zero - // Maybe bug in the upstream library? - // TODO: Look into this. - if server.ServerType.Cores >= 16 && - server.ServerType.Memory >= 32.0 && - server.ServerType.Disk >= 320 { - return amount, server.ServerType, nil - } + if len(serverType) != 0 { + if server.ServerType.Name == serverType { + return amount, server.ServerType, nil + } + } else { + // For Some Reason, These all returns Zero + // Maybe bug in the upstream library? + // TODO: Look into this. + if server.ServerType.Cores >= 8 && + server.ServerType.Memory >= 32.0 && + server.ServerType.Disk >= 240 { + return amount, server.ServerType, nil } } } diff --git a/internal/cmd/get/tail.go b/internal/cmd/get/tail.go index 00a03b4..3e53416 100644 --- a/internal/cmd/get/tail.go +++ b/internal/cmd/get/tail.go @@ -2,9 +2,9 @@ package get import ( "fmt" + "io" "strings" "time" - "io" "golang.org/x/crypto/ssh" ) diff --git a/internal/core/create_server.go b/internal/core/create_server.go index 8c226f8..e6b3bc1 100644 --- a/internal/core/create_server.go +++ b/internal/core/create_server.go @@ -58,6 +58,43 @@ func CreateServer(client *hcloud.Client, server *hcloud.ServerType, serverName s } startAfterCreate := true + automountVol := false + + // We need Special Volume of Size 400 GB + // to hold only the lineage os build, + // this will future proof this app. + volCreateOpts := hcloud.VolumeCreateOpts{ + Name: serverName + "-vol", + Size: 400, + Location: location, + Automount: &automountVol, + } + + err = volCreateOpts.Validate() + if err != nil { + return nil, err + } + + // Create Volume of 400 GiB + volCreateResult, _, err := client.Volume.Create( + context.Background(), + volCreateOpts, + ) + + if err != nil { + return nil, err + } + + // Action Status and Error + ok := false + errMsg := "" + + // Check Current Action First + checkAction(client, volCreateResult.Action, &ok, &errMsg) + if !ok { + return nil, errors.New(errMsg) + } + // Server Creation Options serverCreateOpts := hcloud.ServerCreateOpts{ Name: serverName, @@ -71,10 +108,21 @@ func CreateServer(client *hcloud.Client, server *hcloud.ServerType, serverName s EnableIPv4: true, EnableIPv6: false, }, + Volumes: []*hcloud.Volume{volCreateResult.Volume}, } err = serverCreateOpts.Validate() if err != nil { + // Destroy all Volumes we created before\ + _, volErr := client.Volume.Delete( + context.Background(), + volCreateResult.Volume, + ) + + if volErr != nil { + return nil, err + } + return nil, err } @@ -85,30 +133,42 @@ func CreateServer(client *hcloud.Client, server *hcloud.ServerType, serverName s ) if err != nil { + + // Destroy all Volumes we created before\ + _, volErr := client.Volume.Delete( + context.Background(), + volCreateResult.Volume, + ) + + if volErr != nil { + return nil, err + } + return nil, err } // Wait till we Success or Failure // result from Action that is currently // running. - ok := false - errMsg := "" + ok = false + errMsg = "" // Check Current Action First checkAction(client, createResult.Action, &ok, &errMsg) if !ok { + // Destroy all Volumes we created before\ + _, volErr := client.Volume.Delete( + context.Background(), + volCreateResult.Volume, + ) + + if volErr != nil { + return nil, err + } + return nil, errors.New(errMsg) } - // Loop Over all Next Actions and - // See if it's ok - // for _, action := range createResult.NextActions { - // checkAction(action, &ok, &errMsg) - // if !ok { - // return nil, errors.New(errMsg) - // } - //} - return createResult.Server, nil } @@ -139,7 +199,7 @@ func checkAction(client *hcloud.Client, action *hcloud.Action, ok *bool, errMsg *ok = true } else if targetAction.Status == hcloud.ActionStatusError { *ok = false - *errMsg = "Server Create Action Failed (" + action.ErrorMessage + ")" + *errMsg = "Action Failed (" + action.ErrorMessage + ")" } break } diff --git a/internal/helpers/build.go b/internal/helpers/build.go index f6c5593..8c6676d 100644 --- a/internal/helpers/build.go +++ b/internal/helpers/build.go @@ -34,7 +34,9 @@ func ServerNameFromSHA256(sum string) string { // // "If you are 1 day old then you are dead to me." // -- Antony J.R -func DestroyAllDeadServers(sclient *hcloud.ServerClient) error { +func DestroyAllDeadServers(client *hcloud.Client) error { + sclient := &client.Server + vclient := &client.Volume servers, err := sclient.All( context.Background(), ) @@ -54,6 +56,7 @@ func DestroyAllDeadServers(sclient *hcloud.ServerClient) error { continue } + serverName := server.Name diff := now.Sub(server.Created) hours := int(diff.Hours()) @@ -62,22 +65,37 @@ func DestroyAllDeadServers(sclient *hcloud.ServerClient) error { context.Background(), server, ) + _ = DeleteVolume(vclient, serverName) } } return nil } -func TryDeleteServer(sclient *hcloud.ServerClient, serverName string, maxTries int, interval int) error { +func TryDeleteServer(client *hcloud.Client, serverName string, maxTries int, interval int) error { + sclient := &client.Server + vclient := &client.Volume delTries := 0 for { delErr := DeleteServer(sclient, serverName) - if delErr == nil { - return nil - } - if delErr.Error() == "Server Not Found" { - return nil + if delErr == nil || delErr.Error() == "Server Not Found" { + tries := 0 + + for { + err := DeleteVolume(vclient, serverName) + if err == nil { + break + } + + tries++ + fmt.Println("Destroying Volume Failed. Retrying... ") + time.Sleep(time.Second * time.Duration(interval)) + + if tries > maxTries { + return errors.New("Cannot Destroy Remote Volume. " + err.Error()) + } + } } delTries++ @@ -117,6 +135,35 @@ func DeleteServer(sclient *hcloud.ServerClient, serverName string) error { return errors.New("Server Not Found") } +func DeleteVolume(vclient *hcloud.VolumeClient, serverName string) error { + volName := fmt.Sprintf("%s-vol", serverName) + vols, err := vclient.All( + context.Background(), + ) + + if err != nil { + return err + } + + for _, volume := range vols { + if volume.Name == volName { + _, err := vclient.Delete( + context.Background(), + volume, + ) + + if err != nil { + return err + } + + return nil + + } + } + + return errors.New("Volume Not Found") +} + func GetServerAgeInHours(sclient *hcloud.ServerClient, serverName string) (int, error) { servers, err := sclient.All( context.Background(),