mirror of
https://github.com/cirruslabs/orchard.git
synced 2026-09-30 03:51:43 +02:00
Implement restart policy for VMs (#83)
* Implement restart policy for VMs * Do not update VM.Resource, we only use it as a read-only specification * Err()/setErr(): use atomic.Pointer instead of sync.Mutex
This commit is contained in:
+24
-2
@@ -173,8 +173,9 @@ func (client *Client) request(
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("%w to make a request: %d %s",
|
||||
ErrFailed, response.StatusCode, http.StatusText(response.StatusCode))
|
||||
return fmt.Errorf("%w to make a request: %d %s%s",
|
||||
ErrFailed, response.StatusCode, http.StatusText(response.StatusCode),
|
||||
detailsFromErrorResponseBody(response.Body))
|
||||
}
|
||||
|
||||
if out != nil {
|
||||
@@ -191,6 +192,27 @@ func (client *Client) request(
|
||||
return nil
|
||||
}
|
||||
|
||||
func detailsFromErrorResponseBody(body io.Reader) string {
|
||||
bodyBytes, err := io.ReadAll(body)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var errorResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(bodyBytes, &errorResponse); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if errorResponse.Message != "" {
|
||||
return fmt.Sprintf(" (%s)", errorResponse.Message)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (client *Client) wsRequest(
|
||||
_ context.Context,
|
||||
path string,
|
||||
|
||||
+4
-18
@@ -23,23 +23,23 @@ func (service *VMsService) Create(ctx context.Context, vm *v1.VM) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *VMsService) FindForWorker(ctx context.Context, worker string) (map[string]v1.VM, error) {
|
||||
func (service *VMsService) FindForWorker(ctx context.Context, worker string) ([]v1.VM, error) {
|
||||
allVms, err := service.List(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var filteredVms = make(map[string]v1.VM)
|
||||
var result []v1.VM
|
||||
|
||||
for _, vmResource := range allVms {
|
||||
if vmResource.Worker != worker {
|
||||
continue
|
||||
}
|
||||
|
||||
filteredVms[vmResource.UID] = vmResource
|
||||
result = append(result, vmResource)
|
||||
}
|
||||
|
||||
return filteredVms, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (service *VMsService) List(ctx context.Context) ([]v1.VM, error) {
|
||||
@@ -66,20 +66,6 @@ func (service *VMsService) Get(ctx context.Context, name string) (*v1.VM, error)
|
||||
return &vm, nil
|
||||
}
|
||||
|
||||
func (service *VMsService) Stop(ctx context.Context, name string) (*v1.VM, error) {
|
||||
var vm v1.VM
|
||||
|
||||
err := service.client.request(ctx, http.MethodGet, fmt.Sprintf("vms/%s", name),
|
||||
nil, &vm, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vm.Status = v1.VMStatusStopping
|
||||
|
||||
return service.Update(ctx, vm)
|
||||
}
|
||||
|
||||
func (service *VMsService) Update(ctx context.Context, vm v1.VM) (*v1.VM, error) {
|
||||
var updatedVM v1.VM
|
||||
err := service.client.request(ctx, http.MethodPut, fmt.Sprintf("vms/%s", vm.Name),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var ErrInvalidRestartPolicy = errors.New("invalid restart policy")
|
||||
|
||||
type RestartPolicy string
|
||||
|
||||
const (
|
||||
RestartPolicyNever RestartPolicy = "Never"
|
||||
RestartPolicyOnFailure RestartPolicy = "OnFailure"
|
||||
)
|
||||
|
||||
func NewRestartPolicyFromString(s string) (RestartPolicy, error) {
|
||||
switch s {
|
||||
case string(RestartPolicyNever):
|
||||
return RestartPolicyNever, nil
|
||||
case string(RestartPolicyOnFailure):
|
||||
return RestartPolicyOnFailure, nil
|
||||
default:
|
||||
return "", fmt.Errorf("%w %q", ErrInvalidRestartPolicy, s)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package v1_test
|
||||
|
||||
import (
|
||||
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRestartPolicyFromString(t *testing.T) {
|
||||
_, err := v1.NewRestartPolicyFromString("")
|
||||
assert.Error(t, err, "empty restart policy should yield an error")
|
||||
|
||||
_, err = v1.NewRestartPolicyFromString("non-existent")
|
||||
assert.Error(t, err, "non-existent restart policy should yield an error")
|
||||
|
||||
_, err = v1.NewRestartPolicyFromString("never")
|
||||
assert.Error(t, err, "improperly capitalized but existent policy should yield an error")
|
||||
|
||||
restartPolicy, err := v1.NewRestartPolicyFromString("Never")
|
||||
assert.NoError(t, err, "Never policy should be parsed correctly")
|
||||
assert.Equal(t, v1.RestartPolicyNever, restartPolicy)
|
||||
|
||||
restartPolicy, err = v1.NewRestartPolicyFromString("OnFailure")
|
||||
assert.NoError(t, err, "OnFailure policy should be parsed correctly")
|
||||
assert.Equal(t, v1.RestartPolicyOnFailure, restartPolicy)
|
||||
}
|
||||
+8
-12
@@ -33,10 +33,13 @@ type VM struct {
|
||||
// Worker field is set by the Controller to assign this VM to a specific Worker.
|
||||
Worker string `json:"worker"`
|
||||
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
StartupScript *VMScript `json:"startup_script"`
|
||||
ShutdownScript *VMScript `json:"shutdown_script"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
StartupScript *VMScript `json:"startup_script"`
|
||||
|
||||
RestartPolicy RestartPolicy `json:"restart_policy"`
|
||||
RestartedAt time.Time `json:"restarted_at"`
|
||||
RestartCount uint64 `json:"restart_count"`
|
||||
|
||||
// UID is a useful field for avoiding data races within a single Name.
|
||||
//
|
||||
@@ -67,7 +70,7 @@ type VMScript struct {
|
||||
}
|
||||
|
||||
func (vm VM) TerminalState() bool {
|
||||
return vm.Status == VMStatusStopped || vm.Status == VMStatusFailed
|
||||
return vm.Status == VMStatusFailed
|
||||
}
|
||||
|
||||
type VMStatus string
|
||||
@@ -83,13 +86,6 @@ const (
|
||||
// VMStatusFailed is set by both the Controller and the Worker to indicate a failure
|
||||
// that prevented the VM resource from reaching the VMStatusRunning state.
|
||||
VMStatusFailed VMStatus = "failed"
|
||||
|
||||
// VMStatusStopping is set by the Controller to indicate that a VM resource needs to be stopped but not deleted.
|
||||
VMStatusStopping VMStatus = "stopping"
|
||||
|
||||
// VMStatusStopped is set by both the Worker to indicate that a particular VM resource has been stopped successfully
|
||||
// (either via API or from within a VM via `sudo shutdown -now`).
|
||||
VMStatusStopped VMStatus = "stopped"
|
||||
)
|
||||
|
||||
type ControllerInfo struct {
|
||||
|
||||
Reference in New Issue
Block a user