mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-04 20:06:18 +02:00
feat: add support for HTTP Probes
- Add HTTPProbeSpec to ProbeSpecSpec (URL + timeout) - Implement probeHTTP() to send GET requests, treat 2xx/3xx as success - Support machine proxy config via httpdefaults.PatchTransport - Add HTTPProbeConfig v1alpha1 document and controller integration - Add unit and integration tests for HTTP probe lifecycle Signed-off-by: Pranav Patil <pranavppatil767@gmail.com> Co-authored-by: Pranav Patil <pranavppatil767@gmail.com> Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
This commit is contained in:
parent
9b776d5981
commit
876f836430
@ -237,6 +237,14 @@ message EthernetStatusSpec {
|
||||
repeated talos.resource.definitions.enums.NethelpersWOLMode wake_on_lan = 10;
|
||||
}
|
||||
|
||||
// HTTPProbeSpec describes the HTTP Probe.
|
||||
message HTTPProbeSpec {
|
||||
// URL to probe: http:// or https:// URL.
|
||||
common.URL url = 1;
|
||||
// Timeout for the probe.
|
||||
google.protobuf.Duration timeout = 2;
|
||||
}
|
||||
|
||||
// HardwareAddrSpec describes spec for the link.
|
||||
message HardwareAddrSpec {
|
||||
// Name defines link name
|
||||
@ -502,10 +510,12 @@ message ProbeSpecSpec {
|
||||
google.protobuf.Duration interval = 1;
|
||||
// FailureThreshold is the number of consecutive failures for the probe to be considered failed after having succeeded.
|
||||
int64 failure_threshold = 2;
|
||||
// One of the probe types should be specified, for now it's only TCP.
|
||||
// TCP is the TCP probe spec. One of TCP or HTTP must be specified.
|
||||
TCPProbeSpec tcp = 3;
|
||||
// Configuration layer.
|
||||
talos.resource.definitions.enums.NetworkConfigLayer config_layer = 4;
|
||||
// HTTP is the HTTP probe spec. One of TCP or HTTP must be specified.
|
||||
HTTPProbeSpec http = 5;
|
||||
}
|
||||
|
||||
// ProbeStatusSpec describes the Probe.
|
||||
|
||||
@ -51,6 +51,13 @@ The default installer image has been updated to use the Image Factory.
|
||||
title = "Host DNS Configuration"
|
||||
description = """\
|
||||
HostDNS configuration was moved from the v1alpha1 config `.machine.features.hostDNS` field to the new `hostDNS` in the `ResolverConfig` document.
|
||||
"""
|
||||
|
||||
[notes.httpProbe]
|
||||
title = "HTTP Probe Support"
|
||||
description = """\
|
||||
Talos now supports HTTP network probes, allowing for monitoring of HTTP endpoints.
|
||||
HTTP responses with status 200-399 are considered successful, while connection and transport errors are treated as failures.
|
||||
"""
|
||||
|
||||
[make_deps]
|
||||
|
||||
@ -9,13 +9,16 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/siderolabs/gen/channel"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/httpdefaults"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
)
|
||||
|
||||
@ -121,11 +124,11 @@ func (runner *Runner) run(ctx context.Context, notifyCh chan<- Notification, log
|
||||
|
||||
// probe runs a probe.
|
||||
func (runner *Runner) probe(ctx context.Context) error {
|
||||
var zeroTCP network.TCPProbeSpec
|
||||
|
||||
switch {
|
||||
case runner.Spec.TCP != zeroTCP:
|
||||
case runner.Spec.TCP != (network.TCPProbeSpec{}):
|
||||
return runner.probeTCP(ctx)
|
||||
case runner.Spec.HTTP != (network.HTTPProbeSpec{}):
|
||||
return runner.probeHTTP(ctx)
|
||||
default:
|
||||
return errors.New("no probe type specified")
|
||||
}
|
||||
@ -152,3 +155,36 @@ func (runner *Runner) probeTCP(ctx context.Context) error {
|
||||
|
||||
return conn.Close()
|
||||
}
|
||||
|
||||
// probeHTTP runs an HTTP probe.
|
||||
//
|
||||
// HTTP responses with status 200-399 are considered success.
|
||||
// Status 400+ and connection/transport errors are treated as failures.
|
||||
// The client honors the machine's proxy configuration via httpdefaults.PatchTransport.
|
||||
func (runner *Runner) probeHTTP(ctx context.Context) error {
|
||||
client := &http.Client{
|
||||
Transport: httpdefaults.PatchTransport(cleanhttp.DefaultTransport()),
|
||||
CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, runner.Spec.HTTP.Timeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, runner.Spec.HTTP.URL.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest {
|
||||
return errors.New("received non-success status code: " + resp.Status)
|
||||
}
|
||||
|
||||
return resp.Body.Close()
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"testing/synctest"
|
||||
"time"
|
||||
@ -21,6 +22,18 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
)
|
||||
|
||||
type swapHandler struct {
|
||||
v atomic.Value // stores http.Handler
|
||||
}
|
||||
|
||||
func (h *swapHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.v.Load().(http.Handler).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *swapHandler) Swap(newHandler http.Handler) {
|
||||
h.v.Store(newHandler)
|
||||
}
|
||||
|
||||
func TestProbeHTTP(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
@ -151,3 +164,75 @@ func TestProbeConsecutiveFailures(t *testing.T) {
|
||||
assert.False(t, notify.Status.Success)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProbeHTTPProbe(t *testing.T) {
|
||||
// Server returns 200 OK.
|
||||
handler := &swapHandler{}
|
||||
handler.Swap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
server := httptest.NewServer(handler)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
probeURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
p := probe.Runner{
|
||||
ID: "http-test",
|
||||
Spec: network.ProbeSpecSpec{
|
||||
Interval: 10 * time.Millisecond,
|
||||
HTTP: network.HTTPProbeSpec{
|
||||
URL: probeURL,
|
||||
Timeout: time.Second,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
notifyCh := make(chan probe.Notification)
|
||||
|
||||
p.Start(ctx, notifyCh, zaptest.NewLogger(t))
|
||||
t.Cleanup(p.Stop)
|
||||
|
||||
// probe should succeed — 2xx/3xx responses count as success
|
||||
for range 3 {
|
||||
assert.Equal(t, probe.Notification{
|
||||
ID: "http-test",
|
||||
Status: network.ProbeStatusSpec{
|
||||
Success: true,
|
||||
},
|
||||
}, <-notifyCh)
|
||||
}
|
||||
|
||||
// 4xx/5xx responses count as failure
|
||||
handler.Swap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}))
|
||||
|
||||
for range 3 {
|
||||
notification := <-notifyCh
|
||||
assert.Equal(t, "http-test", notification.ID)
|
||||
assert.False(t, notification.Status.Success)
|
||||
assert.NotEmpty(t, notification.Status.LastError)
|
||||
}
|
||||
|
||||
// stop the server — now the probe should fail
|
||||
server.Close()
|
||||
|
||||
for {
|
||||
notification := <-notifyCh
|
||||
|
||||
if notification.Status.Success {
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Equal(t, "http-test", notification.ID)
|
||||
assert.False(t, notification.Status.Success)
|
||||
assert.NotEmpty(t, notification.Status.LastError)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,6 +131,11 @@ func (ctrl *ProbeConfigController) parseMachineConfiguration(cfg *config.Machine
|
||||
Endpoint: probeConfig.Endpoint(),
|
||||
Timeout: probeConfig.Timeout(),
|
||||
}
|
||||
case configconfig.NetworkHTTPProbeConfig:
|
||||
spec.HTTP = network.HTTPProbeSpec{
|
||||
URL: probeConfig.URL().URL,
|
||||
Timeout: probeConfig.Timeout(),
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported probe config type: %T", probeConfig))
|
||||
}
|
||||
|
||||
@ -5,16 +5,19 @@
|
||||
package network_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
|
||||
"github.com/siderolabs/gen/ensure"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
|
||||
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
@ -29,7 +32,7 @@ func (suite *ProbeConfigSuite) TestNoConfig() {
|
||||
ctest.AssertNoResource[*network.ProbeSpec](suite, "tcp:proxy.example.com:3128", rtestutils.WithNamespace(network.NamespaceName))
|
||||
}
|
||||
|
||||
func (suite *ProbeConfigSuite) TestSingleProbe() {
|
||||
func (suite *ProbeConfigSuite) TestSingleProbe() { //nolint:dupl
|
||||
probeConfig := networkcfg.NewTCPProbeConfigV1Alpha1("proxy-check")
|
||||
probeConfig.ProbeInterval = time.Second
|
||||
probeConfig.ProbeFailureThreshold = 3
|
||||
@ -132,6 +135,58 @@ func (suite *ProbeConfigSuite) TestMultipleProbes() {
|
||||
ctest.AssertNoResource[*network.ProbeSpec](suite, "configuration/tcp:8.8.8.8:53", rtestutils.WithNamespace(network.ConfigNamespaceName))
|
||||
}
|
||||
|
||||
func (suite *ProbeConfigSuite) TestHTTPProbe() { //nolint:dupl
|
||||
probeConfig := networkcfg.NewHTTPProbeConfigV1Alpha1("http-check")
|
||||
probeConfig.ProbeInterval = time.Second
|
||||
probeConfig.ProbeFailureThreshold = 3
|
||||
probeConfig.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse("https://example.com"))}
|
||||
probeConfig.HTTPTimeout = 10 * time.Second
|
||||
|
||||
ctr, err := container.New(probeConfig)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cfg := config.NewMachineConfig(ctr)
|
||||
suite.Create(cfg)
|
||||
|
||||
ctest.AssertResources(
|
||||
suite,
|
||||
[]string{
|
||||
"configuration/http:https://example.com",
|
||||
}, func(r *network.ProbeSpec, asrt *assert.Assertions) {
|
||||
asrt.Equal(time.Second, r.TypedSpec().Interval)
|
||||
asrt.Equal(3, r.TypedSpec().FailureThreshold)
|
||||
asrt.Equal("https://example.com", r.TypedSpec().HTTP.URL.String())
|
||||
asrt.Equal(10*time.Second, r.TypedSpec().HTTP.Timeout)
|
||||
asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer)
|
||||
},
|
||||
rtestutils.WithNamespace(network.ConfigNamespaceName),
|
||||
)
|
||||
|
||||
// Update the probe config
|
||||
ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error {
|
||||
docs := r.Container().Documents()
|
||||
probeDoc := docs[0].(*networkcfg.HTTPProbeConfigV1Alpha1)
|
||||
probeDoc.ProbeFailureThreshold = 5
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
ctest.AssertResources(
|
||||
suite,
|
||||
[]string{
|
||||
"configuration/http:https://example.com",
|
||||
}, func(r *network.ProbeSpec, asrt *assert.Assertions) {
|
||||
asrt.Equal(5, r.TypedSpec().FailureThreshold)
|
||||
},
|
||||
rtestutils.WithNamespace(network.ConfigNamespaceName),
|
||||
)
|
||||
|
||||
// Remove the config
|
||||
suite.Destroy(cfg)
|
||||
|
||||
ctest.AssertNoResource[*network.ProbeSpec](suite, "configuration/http:https://example.com", rtestutils.WithNamespace(network.ConfigNamespaceName))
|
||||
}
|
||||
|
||||
func TestProbeConfigSuite(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@ -8,14 +8,17 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"github.com/siderolabs/gen/ensure"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/siderolabs/talos/internal/integration/base"
|
||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
networkres "github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
)
|
||||
@ -51,7 +54,7 @@ func (suite *ProbeConfigSuite) TearDownTest() {
|
||||
}
|
||||
|
||||
// TestProbeConfig tests that ProbeConfig documents create ProbeSpec resources.
|
||||
func (suite *ProbeConfigSuite) TestProbeConfig() {
|
||||
func (suite *ProbeConfigSuite) TestProbeConfig() { //nolint:dupl
|
||||
node := suite.RandomDiscoveredNodeInternalIP()
|
||||
nodeCtx := client.WithNode(suite.ctx, node)
|
||||
|
||||
@ -67,6 +70,7 @@ func (suite *ProbeConfigSuite) TestProbeConfig() {
|
||||
suite.PatchMachineConfig(nodeCtx, probeConfig)
|
||||
|
||||
// Wait for ProbeSpec resource to be created
|
||||
//nolint:dupl
|
||||
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS,
|
||||
func(spec *networkres.ProbeSpec, asrt *assert.Assertions) {
|
||||
asrt.Equal(2*time.Second, spec.TypedSpec().Interval)
|
||||
@ -195,6 +199,76 @@ func (suite *ProbeConfigSuite) TestProbeStatus() {
|
||||
rtestutils.AssertNoResource[*networkres.ProbeSpec](nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS)
|
||||
}
|
||||
|
||||
// TestHTTPProbeConfig tests that HTTPProbeConfig documents create ProbeSpec resources and run probes.
|
||||
func (suite *ProbeConfigSuite) TestHTTPProbeConfig() {
|
||||
if suite.Airgapped {
|
||||
suite.T().Skip("skipping test in airgapped mode")
|
||||
}
|
||||
|
||||
node := suite.RandomDiscoveredNodeInternalIP()
|
||||
nodeCtx := client.WithNode(suite.ctx, node)
|
||||
|
||||
suite.T().Logf("testing HTTPProbeConfig on node %q", node)
|
||||
|
||||
const probeURL = "https://example.com"
|
||||
|
||||
probeConfig := network.NewHTTPProbeConfigV1Alpha1("http-test-probe")
|
||||
probeConfig.ProbeInterval = 1 * time.Second
|
||||
probeConfig.ProbeFailureThreshold = 3
|
||||
probeConfig.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(probeURL))}
|
||||
probeConfig.HTTPTimeout = 5 * time.Second
|
||||
|
||||
suite.PatchMachineConfig(nodeCtx, probeConfig)
|
||||
|
||||
// Wait for ProbeSpec resource to be created
|
||||
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, "http:"+probeURL,
|
||||
func(spec *networkres.ProbeSpec, asrt *assert.Assertions) {
|
||||
asrt.Equal(1*time.Second, spec.TypedSpec().Interval)
|
||||
asrt.Equal(3, spec.TypedSpec().FailureThreshold)
|
||||
asrt.Equal(probeURL, spec.TypedSpec().HTTP.URL.String())
|
||||
asrt.Equal(5*time.Second, spec.TypedSpec().HTTP.Timeout)
|
||||
asrt.Equal(networkres.ConfigMachineConfiguration, spec.TypedSpec().ConfigLayer)
|
||||
},
|
||||
)
|
||||
|
||||
// Update the probe config
|
||||
probeConfig.ProbeFailureThreshold = 5
|
||||
suite.PatchMachineConfig(nodeCtx, probeConfig)
|
||||
|
||||
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, "http:"+probeURL,
|
||||
func(spec *networkres.ProbeSpec, asrt *assert.Assertions) {
|
||||
asrt.Equal(5, spec.TypedSpec().FailureThreshold)
|
||||
},
|
||||
)
|
||||
|
||||
// Give the probe controller time to run at least one probe
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
// Verify ProbeStatus is created and has been updated
|
||||
probeStatuses, err := safe.StateListAll[*networkres.ProbeStatus](nodeCtx, suite.Client.COSI)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
var found bool
|
||||
|
||||
for status := range probeStatuses.All() {
|
||||
if status.Metadata().ID() == "http:"+probeURL {
|
||||
found = true
|
||||
|
||||
suite.T().Logf("HTTP ProbeStatus: success=%v, lastError=%s", status.TypedSpec().Success, status.TypedSpec().LastError)
|
||||
suite.Assert().True(status.TypedSpec().Success || status.TypedSpec().LastError != "")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
suite.Assert().True(found, "expected to find ProbeStatus for http:"+probeURL)
|
||||
|
||||
// Remove the ProbeConfig
|
||||
suite.RemoveMachineConfigDocuments(nodeCtx, network.HTTPProbeKind)
|
||||
|
||||
rtestutils.AssertNoResource[*networkres.ProbeSpec](nodeCtx, suite.T(), suite.Client.COSI, "http:"+probeURL)
|
||||
}
|
||||
|
||||
func init() {
|
||||
allSuites = append(allSuites, &ProbeConfigSuite{})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1541,6 +1541,71 @@ func (m *EthernetStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *HTTPProbeSpec) MarshalVT() (dAtA []byte, err error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
size := m.SizeVT()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *HTTPProbeSpec) MarshalToVT(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVT(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *HTTPProbeSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
if m == nil {
|
||||
return 0, nil
|
||||
}
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.unknownFields != nil {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Timeout != nil {
|
||||
size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVT(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.Url != nil {
|
||||
if vtmsg, ok := interface{}(m.Url).(interface {
|
||||
MarshalToSizedBufferVT([]byte) (int, error)
|
||||
}); ok {
|
||||
size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
} else {
|
||||
encoded, err := proto.Marshal(m.Url)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= len(encoded)
|
||||
copy(dAtA[i:], encoded)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded)))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *HardwareAddrSpec) MarshalVT() (dAtA []byte, err error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
@ -3573,6 +3638,16 @@ func (m *ProbeSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Http != nil {
|
||||
size, err := m.Http.MarshalToSizedBufferVT(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0x2a
|
||||
}
|
||||
if m.ConfigLayer != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer))
|
||||
i--
|
||||
@ -5707,6 +5782,30 @@ func (m *EthernetStatusSpec) SizeVT() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *HTTPProbeSpec) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.Url != nil {
|
||||
if size, ok := interface{}(m.Url).(interface {
|
||||
SizeVT() int
|
||||
}); ok {
|
||||
l = size.SizeVT()
|
||||
} else {
|
||||
l = proto.Size(m.Url)
|
||||
}
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.Timeout != nil {
|
||||
l = (*durationpb.Duration)(m.Timeout).SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *HardwareAddrSpec) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
@ -6522,6 +6621,10 @@ func (m *ProbeSpecSpec) SizeVT() (n int) {
|
||||
if m.ConfigLayer != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer))
|
||||
}
|
||||
if m.Http != nil {
|
||||
l = m.Http.SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@ -10925,6 +11028,137 @@ func (m *EthernetStatusSpec) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *HTTPProbeSpec) UnmarshalVT(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: HTTPProbeSpec: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: HTTPProbeSpec: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Url == nil {
|
||||
m.Url = &common.URL{}
|
||||
}
|
||||
if unmarshal, ok := interface{}(m.Url).(interface {
|
||||
UnmarshalVT([]byte) error
|
||||
}); ok {
|
||||
if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Url); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Timeout == nil {
|
||||
m.Timeout = &durationpb1.Duration{}
|
||||
}
|
||||
if err := (*durationpb.Duration)(m.Timeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *HardwareAddrSpec) UnmarshalVT(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
@ -16033,6 +16267,42 @@ func (m *ProbeSpecSpec) UnmarshalVT(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Http", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Http == nil {
|
||||
m.Http = &HTTPProbeSpec{}
|
||||
}
|
||||
if err := m.Http.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/siderolabs/gen/optional"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/cel"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
)
|
||||
|
||||
@ -388,3 +389,10 @@ type NetworkHostDNSConfig interface {
|
||||
ForwardKubeDNSToHost() bool
|
||||
ResolveMemberNames() bool
|
||||
}
|
||||
|
||||
// NetworkHTTPProbeConfig defines an HTTP probe configuration.
|
||||
type NetworkHTTPProbeConfig interface {
|
||||
NetworkCommonProbeConfig
|
||||
URL() meta.URL
|
||||
Timeout() time.Duration
|
||||
}
|
||||
|
||||
@ -2226,6 +2226,75 @@
|
||||
],
|
||||
"description": "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\\n"
|
||||
},
|
||||
"network.HTTPProbeConfigV1Alpha1": {
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"v1alpha1"
|
||||
],
|
||||
"title": "apiVersion",
|
||||
"description": "apiVersion is the API version of the resource.\n",
|
||||
"markdownDescription": "apiVersion is the API version of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"kind": {
|
||||
"enum": [
|
||||
"HTTPProbeConfig"
|
||||
],
|
||||
"title": "kind",
|
||||
"description": "kind is the kind of the resource.\n",
|
||||
"markdownDescription": "kind is the kind of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "name",
|
||||
"description": "Name of the probe.\n",
|
||||
"markdownDescription": "Name of the probe.",
|
||||
"x-intellij-html-description": "\u003cp\u003eName of the probe.\u003c/p\u003e\n"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string",
|
||||
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
|
||||
"title": "interval",
|
||||
"description": "Interval between probe attempts.\nDefaults to 1s.\n",
|
||||
"markdownDescription": "Interval between probe attempts.\nDefaults to 1s.",
|
||||
"x-intellij-html-description": "\u003cp\u003eInterval between probe attempts.\nDefaults to 1s.\u003c/p\u003e\n"
|
||||
},
|
||||
"failureThreshold": {
|
||||
"type": "integer",
|
||||
"title": "failureThreshold",
|
||||
"description": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\n",
|
||||
"markdownDescription": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).",
|
||||
"x-intellij-html-description": "\u003cp\u003eNumber of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\u003c/p\u003e\n"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"pattern": "^(http|https)://",
|
||||
"title": "url",
|
||||
"description": "HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.\n",
|
||||
"markdownDescription": "HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.",
|
||||
"x-intellij-html-description": "\u003cp\u003eHTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.\u003c/p\u003e\n"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "string",
|
||||
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
|
||||
"title": "timeout",
|
||||
"description": "Timeout for the probe.\nDefaults to 10s.\n",
|
||||
"markdownDescription": "Timeout for the probe.\nDefaults to 10s.",
|
||||
"x-intellij-html-description": "\u003cp\u003eTimeout for the probe.\nDefaults to 10s.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"required": [
|
||||
"apiVersion",
|
||||
"kind",
|
||||
"name",
|
||||
"url"
|
||||
],
|
||||
"description": "HTTPProbeConfig is a config document to configure network HTTP connectivity probes."
|
||||
},
|
||||
"network.HostDNSConfig": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
@ -5791,6 +5860,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/network.HostnameConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/network.HTTPProbeConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/network.KubeSpanConfigV1Alpha1"
|
||||
},
|
||||
|
||||
@ -2,12 +2,13 @@
|
||||
// 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/.
|
||||
|
||||
// Code generated by "deep-copy -type BlackholeRouteConfigV1Alpha1 -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type VRFConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubeSpanConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RoutingRuleConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TCPProbeConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
// Code generated by "deep-copy -type BlackholeRouteConfigV1Alpha1 -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type VRFConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HTTPProbeConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubeSpanConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RoutingRuleConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TCPProbeConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
)
|
||||
@ -402,6 +403,20 @@ func (o *HCloudVIPConfigV1Alpha1) DeepCopy() *HCloudVIPConfigV1Alpha1 {
|
||||
return &cp
|
||||
}
|
||||
|
||||
// DeepCopy generates a deep copy of *HTTPProbeConfigV1Alpha1.
|
||||
func (o *HTTPProbeConfigV1Alpha1) DeepCopy() *HTTPProbeConfigV1Alpha1 {
|
||||
var cp HTTPProbeConfigV1Alpha1 = *o
|
||||
if o.HTTPEndpoint.URL != nil {
|
||||
cp.HTTPEndpoint.URL = new(url.URL)
|
||||
*cp.HTTPEndpoint.URL = *o.HTTPEndpoint.URL
|
||||
if o.HTTPEndpoint.URL.User != nil {
|
||||
cp.HTTPEndpoint.URL.User = new(url.Userinfo)
|
||||
*cp.HTTPEndpoint.URL.User = *o.HTTPEndpoint.URL.User
|
||||
}
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
// DeepCopy generates a deep copy of *HostnameConfigV1Alpha1.
|
||||
func (o *HostnameConfigV1Alpha1) DeepCopy() *HostnameConfigV1Alpha1 {
|
||||
var cp HostnameConfigV1Alpha1 = *o
|
||||
|
||||
165
pkg/machinery/config/types/network/http_probe.go
Normal file
165
pkg/machinery/config/types/network/http_probe.go
Normal file
@ -0,0 +1,165 @@
|
||||
// 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 network
|
||||
|
||||
//docgen:jsonschema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/siderolabs/gen/ensure"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/internal/registry"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/validation"
|
||||
)
|
||||
|
||||
// HTTPProbeKind is a HTTPProbe config document kind.
|
||||
const HTTPProbeKind = "HTTPProbeConfig"
|
||||
|
||||
func init() {
|
||||
registry.Register(HTTPProbeKind, func(version string) config.Document {
|
||||
switch version {
|
||||
case "v1alpha1": //nolint:goconst
|
||||
return &HTTPProbeConfigV1Alpha1{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Check interfaces.
|
||||
var (
|
||||
_ config.NamedDocument = &HTTPProbeConfigV1Alpha1{}
|
||||
_ config.Validator = &HTTPProbeConfigV1Alpha1{}
|
||||
_ config.NetworkHTTPProbeConfig = &HTTPProbeConfigV1Alpha1{}
|
||||
)
|
||||
|
||||
// HTTPProbeConfigV1Alpha1 is a config document to configure network HTTP connectivity probes.
|
||||
//
|
||||
// examples:
|
||||
// - value: exampleHTTPProbeConfigV1Alpha1()
|
||||
// alias: HTTPProbeConfig
|
||||
// schemaRoot: true
|
||||
// schemaMeta: v1alpha1/HTTPProbeConfig
|
||||
type HTTPProbeConfigV1Alpha1 struct {
|
||||
meta.Meta `yaml:",inline"`
|
||||
|
||||
// description: |
|
||||
// Name of the probe.
|
||||
// examples:
|
||||
// - value: >
|
||||
// "http-check"
|
||||
// schemaRequired: true
|
||||
MetaName string `yaml:"name"`
|
||||
//nolint:embeddedstructfieldcheck
|
||||
CommonProbeConfig `yaml:",inline"`
|
||||
// description: |
|
||||
// HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.
|
||||
// Probe does not follow redirects.
|
||||
// examples:
|
||||
// - value: >
|
||||
// "https://example.com"
|
||||
// schema:
|
||||
// type: string
|
||||
// pattern: "^(http|https)://"
|
||||
// schemaRequired: true
|
||||
HTTPEndpoint meta.URL `yaml:"url"`
|
||||
// description: |
|
||||
// Timeout for the probe.
|
||||
// Defaults to 10s.
|
||||
// examples:
|
||||
// - value: >
|
||||
// 10 * time.Second
|
||||
// schema:
|
||||
// type: string
|
||||
// pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$
|
||||
HTTPTimeout time.Duration `yaml:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// NewHTTPProbeConfigV1Alpha1 creates a new HTTPProbeConfigV1Alpha1 config document.
|
||||
func NewHTTPProbeConfigV1Alpha1(name string) *HTTPProbeConfigV1Alpha1 {
|
||||
return &HTTPProbeConfigV1Alpha1{
|
||||
Meta: meta.Meta{
|
||||
MetaKind: HTTPProbeKind,
|
||||
MetaAPIVersion: "v1alpha1",
|
||||
},
|
||||
MetaName: name,
|
||||
}
|
||||
}
|
||||
|
||||
// Name implements config.NamedDocument interface.
|
||||
func (p *HTTPProbeConfigV1Alpha1) Name() string {
|
||||
return p.MetaName
|
||||
}
|
||||
|
||||
// Clone implements config.Document interface.
|
||||
func (p *HTTPProbeConfigV1Alpha1) Clone() config.Document {
|
||||
return p.DeepCopy()
|
||||
}
|
||||
|
||||
// Validate implements config.Validator interface.
|
||||
func (p *HTTPProbeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) {
|
||||
var (
|
||||
errs error
|
||||
warnings []string //nolint:prealloc
|
||||
)
|
||||
|
||||
if p.MetaName == "" {
|
||||
errs = errors.Join(errs, errors.New("probe name is required"))
|
||||
}
|
||||
|
||||
if p.HTTPEndpoint.URL == nil {
|
||||
errs = errors.Join(errs, errors.New("HTTP probe URL is required"))
|
||||
} else {
|
||||
if p.HTTPEndpoint.URL.Scheme != "http" && p.HTTPEndpoint.URL.Scheme != "https" {
|
||||
errs = errors.Join(errs, fmt.Errorf("HTTP probe URL scheme must be http or https, got %q", p.HTTPEndpoint.URL.Scheme))
|
||||
} else if p.HTTPEndpoint.URL.Opaque != "" || p.HTTPEndpoint.URL.Hostname() == "" {
|
||||
errs = errors.Join(errs, errors.New("HTTP probe URL must be an absolute http or https URL with a non-empty host"))
|
||||
}
|
||||
}
|
||||
|
||||
if p.HTTPTimeout < 0 {
|
||||
errs = errors.Join(errs, fmt.Errorf("HTTP probe timeout cannot be negative: %s", p.HTTPTimeout))
|
||||
}
|
||||
|
||||
extraWarnings, extraErrs := p.CommonProbeConfig.Validate()
|
||||
errs = errors.Join(errs, extraErrs)
|
||||
|
||||
warnings = append(warnings, extraWarnings...)
|
||||
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
func exampleHTTPProbeConfigV1Alpha1() *HTTPProbeConfigV1Alpha1 {
|
||||
cfg := NewHTTPProbeConfigV1Alpha1("http-check")
|
||||
|
||||
cfg.CommonProbeConfig = CommonProbeConfig{
|
||||
ProbeInterval: time.Second,
|
||||
ProbeFailureThreshold: 3,
|
||||
}
|
||||
cfg.HTTPEndpoint.URL = ensure.Value(url.Parse("https://example.com"))
|
||||
cfg.HTTPTimeout = 10 * time.Second
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// URL implements config.NetworkHTTPProbeConfig interface.
|
||||
func (p *HTTPProbeConfigV1Alpha1) URL() meta.URL {
|
||||
return p.HTTPEndpoint
|
||||
}
|
||||
|
||||
// Timeout implements config.NetworkHTTPProbeConfig interface.
|
||||
func (p *HTTPProbeConfigV1Alpha1) Timeout() time.Duration {
|
||||
if p.HTTPTimeout == 0 {
|
||||
return 10 * time.Second
|
||||
}
|
||||
|
||||
return p.HTTPTimeout
|
||||
}
|
||||
256
pkg/machinery/config/types/network/http_probe_test.go
Normal file
256
pkg/machinery/config/types/network/http_probe_test.go
Normal file
@ -0,0 +1,256 @@
|
||||
// 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 network_test
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/siderolabs/gen/ensure"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/configloader"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
)
|
||||
|
||||
//go:embed testdata/httpprobeconfig.yaml
|
||||
var expectedHTTPProbeConfigDocument []byte
|
||||
|
||||
const exampleHTTPURL = "https://example.com"
|
||||
|
||||
func TestHTTPProbeConfigMarshalStability(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := network.NewHTTPProbeConfigV1Alpha1("http-check")
|
||||
cfg.CommonProbeConfig = network.CommonProbeConfig{
|
||||
ProbeInterval: time.Second,
|
||||
ProbeFailureThreshold: 3,
|
||||
}
|
||||
cfg.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
cfg.HTTPTimeout = 10 * time.Second
|
||||
|
||||
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Log(string(marshaled))
|
||||
|
||||
assert.Equal(t, expectedHTTPProbeConfigDocument, marshaled)
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func TestHTTPProbeConfigUnmarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
provider, err := configloader.NewFromBytes(expectedHTTPProbeConfigDocument)
|
||||
require.NoError(t, err)
|
||||
|
||||
docs := provider.Documents()
|
||||
require.Len(t, docs, 1)
|
||||
|
||||
assert.Equal(t, &network.HTTPProbeConfigV1Alpha1{
|
||||
Meta: meta.Meta{
|
||||
MetaAPIVersion: "v1alpha1",
|
||||
MetaKind: network.HTTPProbeKind,
|
||||
},
|
||||
MetaName: "http-check",
|
||||
CommonProbeConfig: network.CommonProbeConfig{
|
||||
ProbeInterval: time.Second,
|
||||
ProbeFailureThreshold: 3,
|
||||
},
|
||||
HTTPEndpoint: meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))},
|
||||
HTTPTimeout: 10 * time.Second,
|
||||
}, docs[0])
|
||||
}
|
||||
|
||||
func TestHTTPProbeConfigValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
cfg func() *network.HTTPProbeConfigV1Alpha1
|
||||
|
||||
expectedError string
|
||||
expectedWarnings []string
|
||||
}{
|
||||
{
|
||||
name: "valid config",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("test-probe")
|
||||
c.CommonProbeConfig = network.CommonProbeConfig{
|
||||
ProbeInterval: time.Second,
|
||||
ProbeFailureThreshold: 3,
|
||||
}
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid http url",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("test-probe")
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse("http://proxy.example.com:3128/health"))}
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("")
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: "probe name is required",
|
||||
},
|
||||
{
|
||||
name: "missing URL",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("probe44")
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: "HTTP probe URL is required",
|
||||
},
|
||||
{
|
||||
name: "invalid scheme",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("probe-scheme")
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse("ftp://example.com"))}
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: `HTTP probe URL scheme must be http or https, got "ftp"`,
|
||||
},
|
||||
{
|
||||
name: "negative timeout",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("probe33")
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
c.HTTPTimeout = -5 * time.Second
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: "HTTP probe timeout cannot be negative: -5s",
|
||||
},
|
||||
{
|
||||
name: "negative values",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("probe33")
|
||||
c.CommonProbeConfig.ProbeFailureThreshold = -1
|
||||
c.CommonProbeConfig.ProbeInterval = -time.Second
|
||||
c.HTTPTimeout = -5 * time.Second
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: "HTTP probe timeout cannot be negative: -5s\nprobe interval cannot be negative: -1s\nprobe failure threshold cannot be negative: -1",
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
return network.NewHTTPProbeConfigV1Alpha1("")
|
||||
},
|
||||
expectedError: "probe name is required\nHTTP probe URL is required",
|
||||
},
|
||||
{
|
||||
name: "url without host",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("no-host")
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse("https://"))}
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: "HTTP probe URL must be an absolute http or https URL with a non-empty host",
|
||||
},
|
||||
{
|
||||
name: "opaque url",
|
||||
cfg: func() *network.HTTPProbeConfigV1Alpha1 {
|
||||
c := network.NewHTTPProbeConfigV1Alpha1("opaque")
|
||||
c.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse("http:opaque-url"))}
|
||||
|
||||
return c
|
||||
},
|
||||
expectedError: "HTTP probe URL must be an absolute http or https URL with a non-empty host",
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
warnings, err := test.cfg().Validate(validationMode{})
|
||||
|
||||
assert.Equal(t, test.expectedWarnings, warnings)
|
||||
|
||||
if test.expectedError != "" {
|
||||
assert.EqualError(t, err, test.expectedError)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPProbeConfigMethods(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("URL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := network.NewHTTPProbeConfigV1Alpha1("test")
|
||||
cfg.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
|
||||
assert.Equal(t, meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}, cfg.URL())
|
||||
})
|
||||
|
||||
t.Run("Name", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
probeName := "my-probe"
|
||||
cfg := network.NewHTTPProbeConfigV1Alpha1(probeName)
|
||||
|
||||
assert.Equal(t, probeName, cfg.Name())
|
||||
})
|
||||
|
||||
t.Run("Timeout with default", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := network.NewHTTPProbeConfigV1Alpha1("test")
|
||||
cfg.HTTPTimeout = 0
|
||||
|
||||
assert.Equal(t, 10*time.Second, cfg.Timeout())
|
||||
})
|
||||
|
||||
t.Run("Timeout with custom value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := network.NewHTTPProbeConfigV1Alpha1("test")
|
||||
cfg.HTTPTimeout = 5 * time.Second
|
||||
|
||||
assert.Equal(t, 5*time.Second, cfg.Timeout())
|
||||
})
|
||||
|
||||
t.Run("Clone", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := network.NewHTTPProbeConfigV1Alpha1("clone-test")
|
||||
cfg.CommonProbeConfig = network.CommonProbeConfig{
|
||||
ProbeInterval: 500 * time.Millisecond,
|
||||
ProbeFailureThreshold: 5,
|
||||
}
|
||||
cfg.HTTPEndpoint = meta.URL{URL: ensure.Value(url.Parse(exampleHTTPURL))}
|
||||
cfg.HTTPTimeout = 15 * time.Second
|
||||
|
||||
cloned := cfg.Clone()
|
||||
|
||||
assert.Equal(t, cfg, cloned)
|
||||
assert.NotSame(t, cfg, cloned)
|
||||
})
|
||||
}
|
||||
@ -5,6 +5,6 @@
|
||||
// Package network provides network machine configuration documents.
|
||||
package network
|
||||
|
||||
//go:generate go tool github.com/siderolabs/talos/tools/docgen -output network_doc.go network.go blackhole_route.go bond.go bridge.go vrf.go default_action_config.go dhcp4.go dhcp6.go dummy.go ethernet.go hcloud_vip.go hostname.go kubespan.go kubespan_endpoints.go layer2_vip.go link.go link_alias.go port_range.go resolver.go routing_rule.go rule_config.go static_host.go tcp_probe.go time_sync.go vlan.go wireguard.go
|
||||
//go:generate go tool github.com/siderolabs/talos/tools/docgen -output network_doc.go network.go blackhole_route.go bond.go bridge.go vrf.go default_action_config.go dhcp4.go dhcp6.go dummy.go ethernet.go hcloud_vip.go hostname.go http_probe.go kubespan.go kubespan_endpoints.go layer2_vip.go link.go link_alias.go port_range.go resolver.go routing_rule.go rule_config.go static_host.go tcp_probe.go time_sync.go vlan.go wireguard.go
|
||||
|
||||
//go:generate go tool github.com/siderolabs/deep-copy -type BlackholeRouteConfigV1Alpha1 -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type VRFConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubeSpanConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RoutingRuleConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TCPProbeConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
//go:generate go tool github.com/siderolabs/deep-copy -type BlackholeRouteConfigV1Alpha1 -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type VRFConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HTTPProbeConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubeSpanConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RoutingRuleConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TCPProbeConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
|
||||
@ -997,6 +997,53 @@ func (HostnameConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
return doc
|
||||
}
|
||||
|
||||
func (HTTPProbeConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "HTTPProbeConfig",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "HTTPProbeConfig is a config document to configure network HTTP connectivity probes." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "HTTPProbeConfig is a config document to configure network HTTP connectivity probes.",
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Type: "Meta",
|
||||
Inline: true,
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Type: "string",
|
||||
Note: "",
|
||||
Description: "Name of the probe.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Name of the probe." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Type: "CommonProbeConfig",
|
||||
Inline: true,
|
||||
},
|
||||
{
|
||||
Name: "url",
|
||||
Type: "URL",
|
||||
Note: "",
|
||||
Description: "HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "timeout",
|
||||
Type: "Duration",
|
||||
Note: "",
|
||||
Description: "Timeout for the probe.\nDefaults to 10s.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Timeout for the probe." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
doc.AddExample("", exampleHTTPProbeConfigV1Alpha1())
|
||||
|
||||
doc.Fields[1].AddExample("", "http-check")
|
||||
doc.Fields[3].AddExample("", "https://example.com")
|
||||
doc.Fields[4].AddExample("", 10*time.Second)
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (KubeSpanConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "KubeSpanConfig",
|
||||
@ -1864,6 +1911,10 @@ func (CommonProbeConfig) Doc() *encoder.Doc {
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "CommonProbeConfig holds fields common to all probe types." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "CommonProbeConfig holds fields common to all probe types.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "HTTPProbeConfigV1Alpha1",
|
||||
FieldName: "",
|
||||
},
|
||||
{
|
||||
TypeName: "TCPProbeConfigV1Alpha1",
|
||||
FieldName: "",
|
||||
@ -2190,6 +2241,7 @@ func GetFileDoc() *encoder.FileDoc {
|
||||
EthernetChannelsConfig{}.Doc(),
|
||||
HCloudVIPConfigV1Alpha1{}.Doc(),
|
||||
HostnameConfigV1Alpha1{}.Doc(),
|
||||
HTTPProbeConfigV1Alpha1{}.Doc(),
|
||||
KubeSpanConfigV1Alpha1{}.Doc(),
|
||||
KubeSpanFiltersConfig{}.Doc(),
|
||||
KubespanEndpointsConfigV1Alpha1{}.Doc(),
|
||||
|
||||
@ -40,6 +40,7 @@ func TestTCPProbeConfigMarshalStability(t *testing.T) {
|
||||
assert.Equal(t, expectedTCPProbeConfigDocument, marshaled)
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func TestTCPProbeConfigUnmarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
7
pkg/machinery/config/types/network/testdata/httpprobeconfig.yaml
vendored
Normal file
7
pkg/machinery/config/types/network/testdata/httpprobeconfig.yaml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: HTTPProbeConfig
|
||||
name: http-check
|
||||
interval: 1s
|
||||
failureThreshold: 3
|
||||
url: https://example.com
|
||||
timeout: 10s
|
||||
@ -8,6 +8,7 @@ package network
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
)
|
||||
@ -569,6 +570,14 @@ func (o PlatformConfigSpec) DeepCopy() PlatformConfigSpec {
|
||||
// DeepCopy generates a deep copy of ProbeSpecSpec.
|
||||
func (o ProbeSpecSpec) DeepCopy() ProbeSpecSpec {
|
||||
var cp ProbeSpecSpec = o
|
||||
if o.HTTP.URL != nil {
|
||||
cp.HTTP.URL = new(url.URL)
|
||||
*cp.HTTP.URL = *o.HTTP.URL
|
||||
if o.HTTP.URL.User != nil {
|
||||
cp.HTTP.URL.User = new(url.Userinfo)
|
||||
*cp.HTTP.URL.User = *o.HTTP.URL.User
|
||||
}
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ package network
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/resource"
|
||||
@ -31,21 +32,29 @@ type ProbeSpecSpec struct {
|
||||
Interval time.Duration `yaml:"interval" protobuf:"1"`
|
||||
// FailureThreshold is the number of consecutive failures for the probe to be considered failed after having succeeded.
|
||||
FailureThreshold int `yaml:"failureThreshold" protobuf:"2"`
|
||||
// One of the probe types should be specified, for now it's only TCP.
|
||||
// TCP is the TCP probe spec. One of TCP or HTTP must be specified.
|
||||
TCP TCPProbeSpec `yaml:"tcp,omitempty" protobuf:"3"`
|
||||
// Configuration layer.
|
||||
ConfigLayer ConfigLayer `yaml:"layer" protobuf:"4"`
|
||||
// HTTP is the HTTP probe spec. One of TCP or HTTP must be specified.
|
||||
HTTP HTTPProbeSpec `yaml:"http,omitempty" protobuf:"5"`
|
||||
}
|
||||
|
||||
// ID returns the ID of the resource based on the spec.
|
||||
func (spec *ProbeSpecSpec) ID() (resource.ID, error) {
|
||||
var zeroTCP TCPProbeSpec
|
||||
|
||||
if spec.TCP == zeroTCP {
|
||||
return "", errors.New("no probe type specified")
|
||||
if spec.TCP != zeroTCP {
|
||||
return fmt.Sprintf("tcp:%s", spec.TCP.Endpoint), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("tcp:%s", spec.TCP.Endpoint), nil
|
||||
var zeroHTTP HTTPProbeSpec
|
||||
|
||||
if spec.HTTP != zeroHTTP {
|
||||
return fmt.Sprintf("http:%s", spec.HTTP.URL.String()), nil
|
||||
}
|
||||
|
||||
return "", errors.New("no probe type specified")
|
||||
}
|
||||
|
||||
// Equal returns true if the specs are equal.
|
||||
@ -63,6 +72,16 @@ type TCPProbeSpec struct {
|
||||
Timeout time.Duration `yaml:"timeout" protobuf:"2"`
|
||||
}
|
||||
|
||||
// HTTPProbeSpec describes the HTTP Probe.
|
||||
//
|
||||
//gotagsrewrite:gen
|
||||
type HTTPProbeSpec struct {
|
||||
// URL to probe: http:// or https:// URL.
|
||||
URL *url.URL `yaml:"url" protobuf:"1"`
|
||||
// Timeout for the probe.
|
||||
Timeout time.Duration `yaml:"timeout" protobuf:"2"`
|
||||
}
|
||||
|
||||
// NewProbeSpec initializes a ProbeSpec resource.
|
||||
func NewProbeSpec(namespace resource.Namespace, id resource.ID) *ProbeSpec {
|
||||
return typed.NewResource[ProbeSpecSpec, ProbeSpecExtension](
|
||||
|
||||
@ -21,7 +21,6 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
|
||||
"go.yaml.in/yaml/v4"
|
||||
"mvdan.cc/gofumpt/format"
|
||||
)
|
||||
|
||||
@ -534,6 +534,7 @@ description: Talos gRPC API reference.
|
||||
- [EthernetSpecSpec](#talos.resource.definitions.network.EthernetSpecSpec)
|
||||
- [EthernetSpecSpec.FeaturesEntry](#talos.resource.definitions.network.EthernetSpecSpec.FeaturesEntry)
|
||||
- [EthernetStatusSpec](#talos.resource.definitions.network.EthernetStatusSpec)
|
||||
- [HTTPProbeSpec](#talos.resource.definitions.network.HTTPProbeSpec)
|
||||
- [HardwareAddrSpec](#talos.resource.definitions.network.HardwareAddrSpec)
|
||||
- [HostDNSConfigSpec](#talos.resource.definitions.network.HostDNSConfigSpec)
|
||||
- [HostnameSpecSpec](#talos.resource.definitions.network.HostnameSpecSpec)
|
||||
@ -9373,6 +9374,22 @@ EthernetStatusSpec describes status of rendered secrets.
|
||||
|
||||
|
||||
|
||||
<a name="talos.resource.definitions.network.HTTPProbeSpec"></a>
|
||||
|
||||
### HTTPProbeSpec
|
||||
HTTPProbeSpec describes the HTTP Probe.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| url | [common.URL](#common.URL) | | URL to probe: http:// or https:// URL. |
|
||||
| timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | Timeout for the probe. |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="talos.resource.definitions.network.HardwareAddrSpec"></a>
|
||||
|
||||
### HardwareAddrSpec
|
||||
@ -9870,8 +9887,9 @@ ProbeSpecSpec describes the Probe.
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| interval | [google.protobuf.Duration](#google.protobuf.Duration) | | Interval between the probes. |
|
||||
| failure_threshold | [int64](#int64) | | FailureThreshold is the number of consecutive failures for the probe to be considered failed after having succeeded. |
|
||||
| tcp | [TCPProbeSpec](#talos.resource.definitions.network.TCPProbeSpec) | | One of the probe types should be specified, for now it's only TCP. |
|
||||
| tcp | [TCPProbeSpec](#talos.resource.definitions.network.TCPProbeSpec) | | TCP is the TCP probe spec. One of TCP or HTTP must be specified. |
|
||||
| config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | Configuration layer. |
|
||||
| http | [HTTPProbeSpec](#talos.resource.definitions.network.HTTPProbeSpec) | | HTTP is the HTTP probe spec. One of TCP or HTTP must be specified. |
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
---
|
||||
description: HTTPProbeConfig is a config document to configure network HTTP connectivity probes.
|
||||
title: HTTPProbeConfig
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{{< highlight yaml >}}
|
||||
apiVersion: v1alpha1
|
||||
kind: HTTPProbeConfig
|
||||
name: http-check # Name of the probe.
|
||||
interval: 1s # Interval between probe attempts.
|
||||
failureThreshold: 3 # Number of consecutive failures for the probe to be considered failed after having succeeded.
|
||||
url: https://example.com # HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.
|
||||
timeout: 10s # Timeout for the probe.
|
||||
{{< /highlight >}}
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`name` |string |Name of the probe. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
name: http-check
|
||||
{{< /highlight >}}</details> | |
|
||||
|`interval` |Duration |Interval between probe attempts.<br>Defaults to 1s. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
interval: 1s
|
||||
{{< /highlight >}}</details> | |
|
||||
|`failureThreshold` |int |Number of consecutive failures for the probe to be considered failed after having succeeded.<br>Defaults to 0 (immediately fail on first failure). <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
failureThreshold: 3
|
||||
{{< /highlight >}}</details> | |
|
||||
|`url` |URL |HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.<br>Probe does not follow redirects. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
url: https://example.com
|
||||
{{< /highlight >}}</details> | |
|
||||
|`timeout` |Duration |Timeout for the probe.<br>Defaults to 10s. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
timeout: 10s
|
||||
{{< /highlight >}}</details> | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -2226,6 +2226,75 @@
|
||||
],
|
||||
"description": "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\\n"
|
||||
},
|
||||
"network.HTTPProbeConfigV1Alpha1": {
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"v1alpha1"
|
||||
],
|
||||
"title": "apiVersion",
|
||||
"description": "apiVersion is the API version of the resource.\n",
|
||||
"markdownDescription": "apiVersion is the API version of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"kind": {
|
||||
"enum": [
|
||||
"HTTPProbeConfig"
|
||||
],
|
||||
"title": "kind",
|
||||
"description": "kind is the kind of the resource.\n",
|
||||
"markdownDescription": "kind is the kind of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "name",
|
||||
"description": "Name of the probe.\n",
|
||||
"markdownDescription": "Name of the probe.",
|
||||
"x-intellij-html-description": "\u003cp\u003eName of the probe.\u003c/p\u003e\n"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string",
|
||||
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
|
||||
"title": "interval",
|
||||
"description": "Interval between probe attempts.\nDefaults to 1s.\n",
|
||||
"markdownDescription": "Interval between probe attempts.\nDefaults to 1s.",
|
||||
"x-intellij-html-description": "\u003cp\u003eInterval between probe attempts.\nDefaults to 1s.\u003c/p\u003e\n"
|
||||
},
|
||||
"failureThreshold": {
|
||||
"type": "integer",
|
||||
"title": "failureThreshold",
|
||||
"description": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\n",
|
||||
"markdownDescription": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).",
|
||||
"x-intellij-html-description": "\u003cp\u003eNumber of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\u003c/p\u003e\n"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"pattern": "^(http|https)://",
|
||||
"title": "url",
|
||||
"description": "HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.\n",
|
||||
"markdownDescription": "HTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.",
|
||||
"x-intellij-html-description": "\u003cp\u003eHTTP or HTTPS URL to probe. The probe succeeds if the server responds with a 2xx or 3xx status code.\nProbe does not follow redirects.\u003c/p\u003e\n"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "string",
|
||||
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
|
||||
"title": "timeout",
|
||||
"description": "Timeout for the probe.\nDefaults to 10s.\n",
|
||||
"markdownDescription": "Timeout for the probe.\nDefaults to 10s.",
|
||||
"x-intellij-html-description": "\u003cp\u003eTimeout for the probe.\nDefaults to 10s.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"required": [
|
||||
"apiVersion",
|
||||
"kind",
|
||||
"name",
|
||||
"url"
|
||||
],
|
||||
"description": "HTTPProbeConfig is a config document to configure network HTTP connectivity probes."
|
||||
},
|
||||
"network.HostDNSConfig": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
@ -5791,6 +5860,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/network.HostnameConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/network.HTTPProbeConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/network.KubeSpanConfigV1Alpha1"
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user