mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
Merge pull request #317 from areed/host-remote-port
Analyze TCP connection
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: connect
|
||||
spec:
|
||||
collectors:
|
||||
- tcpConnect:
|
||||
collectorName: weave host 1
|
||||
address: 10.128.0.2:6783
|
||||
analyzers:
|
||||
- tcpConnect:
|
||||
collectorName: weave host 1
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "connection-refused"
|
||||
message: Connection to weave on host 1 was refused
|
||||
- fail:
|
||||
when: "connection-timeout"
|
||||
message: Timed out connecting to weave on host 1
|
||||
- fail:
|
||||
when: "error"
|
||||
message: Unexpected error connecting to weave on host 1
|
||||
- pass:
|
||||
when: "connected"
|
||||
message: Successfully connected to weave on host 1
|
||||
@@ -104,7 +104,13 @@ func HostAnalyze(hostAnalyzer *troubleshootv1beta2.HostAnalyze, getFile getColle
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
|
||||
if hostAnalyzer.TCPConnect != nil {
|
||||
result, err := analyzeHostTCPConnect(hostAnalyzer.TCPConnect, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
return nil, errors.New("invalid analyzer")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
)
|
||||
|
||||
func analyzeHostTCPConnect(hostAnalyzer *troubleshootv1beta2.TCPConnectAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
fullPath := path.Join("connect", fmt.Sprintf("%s.json", hostAnalyzer.CollectorName))
|
||||
|
||||
collected, err := getCollectedFileContents(fullPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
|
||||
}
|
||||
actual := collect.NetworkStatusResult{}
|
||||
if err := json.Unmarshal(collected, &actual); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal collected")
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = "TCP Connection Attempt"
|
||||
}
|
||||
result.Title = title
|
||||
|
||||
for _, outcome := range hostAnalyzer.Outcomes {
|
||||
if outcome.Fail != nil {
|
||||
if outcome.Fail.When == "" {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Fail.When {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
} else if outcome.Warn != nil {
|
||||
if outcome.Warn.When == "" {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Warn.When {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
} else if outcome.Pass != nil {
|
||||
if outcome.Pass.When == "" {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Pass.When {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAnalyzeTCPConnect(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
info *collect.NetworkStatusResult
|
||||
hostAnalyzer *troubleshootv1beta2.TCPConnectAnalyze
|
||||
result *AnalyzeResult
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "connection refused, fail",
|
||||
info: &collect.NetworkStatusResult{
|
||||
Status: collect.NetworkStatusConnectionRefused,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TCPConnectAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "connection-refused",
|
||||
Message: "Connection was refused",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "TCP Connection Attempt",
|
||||
IsFail: true,
|
||||
Message: "Connection was refused",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "connected, fail",
|
||||
info: &collect.NetworkStatusResult{
|
||||
Status: collect.NetworkStatusConnected,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TCPConnectAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "connection-refused",
|
||||
Message: "Connection was refused",
|
||||
},
|
||||
},
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "connected",
|
||||
Message: "Connection was successful",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "TCP Connection Attempt",
|
||||
IsPass: true,
|
||||
Message: "Connection was successful",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
b, err := json.Marshal(test.info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
getCollectedFileContents := func(filename string) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
result, err := analyzeHostTCPConnect(test.hostAnalyzer, getCollectedFileContents)
|
||||
if test.expectErr {
|
||||
req.Error(err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,12 @@ type BlockDevicesAnalyze struct {
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type TCPConnectAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type HostAnalyze struct {
|
||||
CPU *CPUAnalyze `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||
//
|
||||
@@ -67,4 +73,6 @@ type HostAnalyze struct {
|
||||
Time *TimeAnalyze `json:"time" yaml:"time"`
|
||||
|
||||
BlockDevices *BlockDevicesAnalyze `json:"blockDevices" yaml:"blockDevices"`
|
||||
|
||||
TCPConnect *TCPConnectAnalyze `json:"tcpConnect" yaml:"tcpConnect"`
|
||||
}
|
||||
|
||||
@@ -67,6 +67,12 @@ type HostBlockDevices struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type TCPConnect struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
Address string `json:"address"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type HostCollect struct {
|
||||
CPU *CPU `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||
Memory *Memory `json:"memory,omitempty" yaml:"memory,omitempty"`
|
||||
@@ -79,6 +85,7 @@ type HostCollect struct {
|
||||
HTTP *HostHTTP `json:"http,omitempty" yaml:"http,omitempty"`
|
||||
Time *HostTime `json:"time,omitempty" yaml:"time,omitempty"`
|
||||
BlockDevices *HostBlockDevices `json:"blockDevices,omitempty" yaml:"blockDevices,omitempty"`
|
||||
TCPConnect *TCPConnect `json:"tcpConnect,omitempty" yaml:"tcpConnect,omitempty"`
|
||||
}
|
||||
|
||||
func (c *HostCollect) GetName() string {
|
||||
|
||||
@@ -1172,6 +1172,11 @@ func (in *HostAnalyze) DeepCopyInto(out *HostAnalyze) {
|
||||
*out = new(BlockDevicesAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.TCPConnect != nil {
|
||||
in, out := &in.TCPConnect, &out.TCPConnect
|
||||
*out = new(TCPConnectAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostAnalyze.
|
||||
@@ -1258,6 +1263,11 @@ func (in *HostCollect) DeepCopyInto(out *HostCollect) {
|
||||
*out = new(HostBlockDevices)
|
||||
**out = **in
|
||||
}
|
||||
if in.TCPConnect != nil {
|
||||
in, out := &in.TCPConnect, &out.TCPConnect
|
||||
*out = new(TCPConnect)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostCollect.
|
||||
@@ -2347,6 +2357,49 @@ func (in *SupportBundleVersionSpec) DeepCopy() *SupportBundleVersionSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPConnect) DeepCopyInto(out *TCPConnect) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPConnect.
|
||||
func (in *TCPConnect) DeepCopy() *TCPConnect {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TCPConnect)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPConnectAnalyze) DeepCopyInto(out *TCPConnectAnalyze) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPConnectAnalyze.
|
||||
func (in *TCPConnectAnalyze) DeepCopy() *TCPConnectAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TCPConnectAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPLoadBalancer) DeepCopyInto(out *TCPLoadBalancer) {
|
||||
*out = *in
|
||||
|
||||
@@ -36,6 +36,8 @@ func (c *HostCollector) RunCollectorSync() (result map[string][]byte, err error)
|
||||
result, err = HostTime(c)
|
||||
} else if c.Collect.BlockDevices != nil {
|
||||
result, err = HostBlockDevices(c)
|
||||
} else if c.Collect.TCPConnect != nil {
|
||||
result, err = HostTCPConnect(c)
|
||||
} else {
|
||||
err = errors.New("no spec found to run")
|
||||
return
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func HostTCPConnect(c *HostCollector) (map[string][]byte, error) {
|
||||
address := c.Collect.TCPConnect.Address
|
||||
|
||||
timeout := 10 * time.Second
|
||||
if c.Collect.TCPConnect.Timeout != "" {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(c.Collect.TCPConnect.Timeout)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse timeout %q", c.Collect.TCPConnect.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
result := NetworkStatusResult{
|
||||
Status: attemptConnect(address, timeout),
|
||||
}
|
||||
|
||||
b, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal result")
|
||||
}
|
||||
|
||||
name := path.Join("connect", fmt.Sprintf("%s.json", c.Collect.TCPConnect.CollectorName))
|
||||
|
||||
return map[string][]byte{
|
||||
name: b,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func attemptConnect(address string, timeout time.Duration) NetworkStatus {
|
||||
conn, err := net.DialTimeout("tcp", address, timeout)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "i/o timeout") {
|
||||
return NetworkStatusConnectionTimeout
|
||||
}
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return NetworkStatusConnectionRefused
|
||||
}
|
||||
return NetworkStatusErrorOther
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
return NetworkStatusConnected
|
||||
}
|
||||
Reference in New Issue
Block a user