sidero/app/sidero-controller-manager/internal/power/api/api.go
Andrey Smirnov fbcd02a45d
feat: update for Talos 1.3.0
Rename to siderolabs, bump dependencies, controller-runtime, get rid of
netaddr, new SideroLink API, etc.

Use bootstrap cluster with a control plane + worker to avoid nasty
restarts when host-mode SideroLink IP pops up.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2022-12-16 16:35:30 +04:00

142 lines
3.1 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package api provides metal machine management via API.
package api
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
metalv1 "github.com/siderolabs/sidero/app/sidero-controller-manager/api/v1alpha2"
"github.com/siderolabs/sidero/app/sidero-controller-manager/pkg/types"
)
// Client provides management over simple API.
type Client struct {
endpoint string
}
// NewClient returns new API client to manage metal machine.
func NewClient(spec metalv1.ManagementAPI) (*Client, error) {
return &Client{
endpoint: spec.Endpoint,
}, nil
}
// Close the client.
func (c *Client) Close() error {
return nil
}
func (c *Client) postRequest(path string) error {
failureMode := DefaultDice.Roll()
switch failureMode { //nolint:exhaustive
case ExplicitFailure:
return fmt.Errorf("simulated failure from the power management")
case SilentFailure:
// don't do anything
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("http://%s%s", c.endpoint, path), nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if resp.Body != nil {
defer func() {
_, _ = io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("API error: %s", resp.Status)
}
return nil
}
// PowerOn will power on a given machine.
func (c *Client) PowerOn() error {
return c.postRequest("/poweron")
}
// PowerOff will power off a given machine.
func (c *Client) PowerOff() error {
return c.postRequest("/poweroff")
}
// PowerCycle will power cycle a given machine.
func (c *Client) PowerCycle() error {
return c.postRequest("/reboot")
}
// SetPXE makes sure the node will pxe boot next time.
func (c *Client) SetPXE(mode types.PXEMode) error {
// no way to enforce mode via QEMU API
return c.postRequest("/pxeboot")
}
// IsPoweredOn checks current power state.
func (c *Client) IsPoweredOn() (bool, error) {
failureMode := DefaultDice.Roll()
switch failureMode { //nolint:exhaustive
case ExplicitFailure:
return false, fmt.Errorf("simulated failure from the power management")
case SilentFailure:
return time.Now().Second()%2 == 0, nil
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("http://%s/status", c.endpoint), nil)
if err != nil {
return false, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return false, err
}
if resp.Body != nil {
defer func() {
_, _ = io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
}
var status struct {
PoweredOn bool
}
if err = json.NewDecoder(resp.Body).Decode(&status); err != nil {
return false, err
}
return status.PoweredOn, nil
}
// IsFake returns false.
func (c *Client) IsFake() bool {
return false
}