Merge pull request #366 from areed/longhorn

Longhorn collector for all CRDs
This commit is contained in:
Andrew Reed
2021-05-26 18:52:06 -05:00
committed by GitHub
12 changed files with 941 additions and 14 deletions
+1
View File
@@ -27,3 +27,4 @@ vendor
*~
dist
try.sh
+9
View File
@@ -0,0 +1,9 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: longhorn
spec:
collectors:
- longhorn: {}
analyzers:
- longhorn: {}
+27 -2
View File
@@ -6,7 +6,6 @@ require (
cloud.google.com/go v0.57.0 // indirect
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
github.com/aws/aws-sdk-go v1.25.18 // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/chzyer/logex v1.1.11-0.20160617073814-96a4d311aa9b // indirect
github.com/containers/image/v5 v5.10.4
@@ -26,10 +25,10 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.3.0
github.com/longhorn/longhorn-manager v1.1.1
github.com/manifoldco/promptui v0.8.0
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.9
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851
@@ -52,3 +51,29 @@ require (
k8s.io/client-go v0.20.2
sigs.k8s.io/controller-runtime v0.8.3
)
replace (
github.com/longhorn/longhorn-manager => github.com/replicatedhq/longhorn-manager v1.1.2-0.20210526001437-4003f67ebd06
k8s.io/api => k8s.io/api v0.20.2
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.2
k8s.io/apimachinery => k8s.io/apimachinery v0.20.2
k8s.io/apiserver => k8s.io/apiserver v0.20.2
k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.2
k8s.io/client-go => k8s.io/client-go v0.20.2
k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.2
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.2
k8s.io/code-generator => k8s.io/code-generator v0.20.2
k8s.io/component-base => k8s.io/component-base v0.20.2
k8s.io/cri-api => k8s.io/cri-api v0.20.2
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.2
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.2
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.2
k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.2
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.2
k8s.io/kubectl => k8s.io/kubectl v0.20.2
k8s.io/kubelet => k8s.io/kubelet v0.20.2
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.2
k8s.io/metrics => k8s.io/metrics v0.20.2
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.2
sigs.k8s.io/controller-runtime => github.com/kubernetes-sigs/controller-runtime v0.8.3
)
+421 -12
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -299,6 +299,16 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Longhorn != nil {
isExcluded, err := isExcluded(analyzer.Longhorn.Exclude)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
return longhorn(analyzer.Longhorn, getFile, findFiles)
}
if analyzer.RegistryImages != nil {
isExcluded, err := isExcluded(analyzer.RegistryImages.Exclude)
+69
View File
@@ -0,0 +1,69 @@
package analyzer
import (
"fmt"
"path/filepath"
longhornv1beta1 "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta1"
longhorntypes "github.com/longhorn/longhorn-manager/types"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"gopkg.in/yaml.v2"
)
func longhorn(analyzer *troubleshootv1beta2.LonghornAnalyze, getCollectedFileContents func(string) ([]byte, error), findFiles func(string) (map[string][]byte, error)) ([]*AnalyzeResult, error) {
ns := collect.DefaultLonghornNamespace
if analyzer.Namespace != "" {
ns = analyzer.Namespace
}
nodesDir := collect.GetLonghornNodesDirectory(ns)
glob := filepath.Join(nodesDir, "*")
nodesYaml, err := findFiles(glob)
if err != nil {
return nil, errors.Wrapf(err, "failed to find longhorn nodes files under %s", nodesDir)
}
nodes := []*longhornv1beta1.Node{}
for key, nodeYaml := range nodesYaml {
node := &longhornv1beta1.Node{}
err := yaml.Unmarshal(nodeYaml, node)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal node yaml from %s", key)
}
nodes = append(nodes, node)
}
results := []*AnalyzeResult{}
for _, node := range nodes {
results = append(results, analyzeLonghornNodeSchedulable(node))
}
return results, nil
}
func analyzeLonghornNodeSchedulable(node *longhornv1beta1.Node) *AnalyzeResult {
result := &AnalyzeResult{
Title: fmt.Sprintf("Longhorn Node Schedulable: %s", node.Name),
}
for key, condition := range node.Status.Conditions {
if key != longhorntypes.NodeConditionTypeSchedulable {
continue
}
if condition.Status == longhorntypes.ConditionStatusFalse {
result.IsWarn = true
result.Message = "Longhorn node is not schedulable"
return result
} else if condition.Status == longhorntypes.ConditionStatusTrue {
result.IsPass = true
result.Message = "Longhorn node is schedulable"
return result
}
}
result.IsWarn = true
result.Message = "Node schedulable status not found"
return result
}
+66
View File
@@ -0,0 +1,66 @@
package analyzer
import (
"testing"
longhornv1beta1 "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta1"
longhorntypes "github.com/longhorn/longhorn-manager/types"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestAnalyzeLonghornNodeSchedulable(t *testing.T) {
tests := []struct {
name string
node *longhornv1beta1.Node
expect *AnalyzeResult
}{
{
name: "schedulable",
node: &longhornv1beta1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "prod-1",
},
Status: longhorntypes.NodeStatus{
Conditions: map[string]longhorntypes.Condition{
longhorntypes.NodeConditionTypeSchedulable: longhorntypes.Condition{
Status: longhorntypes.ConditionStatusTrue,
},
},
},
},
expect: &AnalyzeResult{
Title: "Longhorn Node Schedulable: prod-1",
Message: "Longhorn node is schedulable",
IsPass: true,
},
},
{
name: "unschedulable",
node: &longhornv1beta1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "prod-1",
},
Status: longhorntypes.NodeStatus{
Conditions: map[string]longhorntypes.Condition{
longhorntypes.NodeConditionTypeSchedulable: longhorntypes.Condition{
Status: longhorntypes.ConditionStatusFalse,
},
},
},
},
expect: &AnalyzeResult{
Title: "Longhorn Node Schedulable: prod-1",
Message: "Longhorn node is not schedulable",
IsWarn: true,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := analyzeLonghornNodeSchedulable(test.node)
assert.Equal(t, test.expect, got)
})
}
}
@@ -116,6 +116,13 @@ type CephStatusAnalyze struct {
Namespace string `json:"namespace" yaml:"namespace"`
}
type LonghornAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
}
type RegistryImagesAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
@@ -144,5 +151,6 @@ type Analyze struct {
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,omitempty"`
CephStatus *CephStatusAnalyze `json:"cephStatus,omitempty" yaml:"cephStatus,omitempty"`
Longhorn *LonghornAnalyze `json:"longhorn,omitempty" yaml:"longhorn,omitempty"`
RegistryImages *RegistryImagesAnalyze `json:"registryImages,omitempty" yaml:"registryImages,omitempty"`
}
@@ -139,6 +139,12 @@ type Ceph struct {
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
}
type Longhorn struct {
CollectorMeta `json:",inline" yaml:",inline"`
Namespace string `json:"namespace" yaml:"namespace"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
}
type RegistryImages struct {
CollectorMeta `json:",inline" yaml:",inline"`
Images []string `json:"images" yaml:"images"`
@@ -161,6 +167,7 @@ type Collect struct {
Redis *Database `json:"redis,omitempty" yaml:"redis,omitempty"`
Collectd *Collectd `json:"collectd,omitempty" yaml:"collectd,omitempty"`
Ceph *Ceph `json:"ceph,omitempty" yaml:"ceph,omitempty"`
Longhorn *Longhorn `json:"longhorn,omitempty" yaml:"longhorn,omitempty"`
RegistryImages *RegistryImages `json:"registryImages,omitempty" yaml:"registryImages,omitempty"`
}
@@ -384,6 +391,10 @@ func (c *Collect) GetName() string {
collector = "ceph"
name = c.Ceph.CollectorName
}
if c.Longhorn != nil {
collector = "longhorn"
name = c.Longhorn.CollectorName
}
if c.RegistryImages != nil {
collector = "registry-images"
name = c.RegistryImages.CollectorName
@@ -132,6 +132,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
*out = new(CephStatusAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Longhorn != nil {
in, out := &in.Longhorn, &out.Longhorn
*out = new(LonghornAnalyze)
(*in).DeepCopyInto(*out)
}
if in.RegistryImages != nil {
in, out := &in.RegistryImages, &out.RegistryImages
*out = new(RegistryImagesAnalyze)
@@ -580,6 +585,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
*out = new(Ceph)
**out = **in
}
if in.Longhorn != nil {
in, out := &in.Longhorn, &out.Longhorn
*out = new(Longhorn)
**out = **in
}
if in.RegistryImages != nil {
in, out := &in.RegistryImages, &out.RegistryImages
*out = new(RegistryImages)
@@ -1810,6 +1820,49 @@ func (in *Logs) DeepCopy() *Logs {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Longhorn) DeepCopyInto(out *Longhorn) {
*out = *in
out.CollectorMeta = in.CollectorMeta
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Longhorn.
func (in *Longhorn) DeepCopy() *Longhorn {
if in == nil {
return nil
}
out := new(Longhorn)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LonghornAnalyze) DeepCopyInto(out *LonghornAnalyze) {
*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 LonghornAnalyze.
func (in *LonghornAnalyze) DeepCopy() *LonghornAnalyze {
if in == nil {
return nil
}
out := new(LonghornAnalyze)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Memory) DeepCopyInto(out *Memory) {
*out = *in
+10
View File
@@ -155,6 +155,14 @@ func (c *Collector) IsExcluded() bool {
if isExcludedResult {
return true
}
} else if c.Collect.Longhorn != nil {
isExcludedResult, err := isExcluded(c.Collect.Longhorn.Exclude)
if err != nil {
return true
}
if isExcludedResult {
return true
}
}
return false
}
@@ -199,6 +207,8 @@ func (c *Collector) RunCollectorSync(globalRedactors []*troubleshootv1beta2.Reda
result, err = Collectd(c, c.Collect.Collectd)
} else if c.Collect.Ceph != nil {
result, err = Ceph(c, c.Collect.Ceph)
} else if c.Collect.Longhorn != nil {
result, err = Longhorn(c, c.Collect.Longhorn)
} else if c.Collect.RegistryImages != nil {
result, err = Registry(c, c.Collect.RegistryImages)
} else {
+256
View File
@@ -0,0 +1,256 @@
package collect
import (
"context"
"fmt"
"path/filepath"
longhornv1beta1 "github.com/longhorn/longhorn-manager/k8s/pkg/client/clientset/versioned/typed/longhorn/v1beta1"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"gopkg.in/yaml.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
DefaultLonghornNamespace = "longhorn-system"
)
func Longhorn(c *Collector, longhornCollector *troubleshootv1beta2.Longhorn) (map[string][]byte, error) {
ctx := context.TODO()
ns := DefaultLonghornNamespace
if longhornCollector.Namespace != "" {
ns = longhornCollector.Namespace
}
client, err := longhornv1beta1.NewForConfig(c.ClientConfig)
if err != nil {
return nil, errors.Wrap(err, "failed to create longhorn client")
}
final := map[string][]byte{}
// collect nodes.longhorn.io
nodes, err := client.Nodes(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list nodes.longhorn.io")
}
dir := GetLonghornNodesDirectory(ns)
for _, node := range nodes.Items {
b, err := yaml.Marshal(node)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal node %s", node.Name)
}
key := filepath.Join(dir, node.Name+".yaml")
final[key] = b
}
// collect volumes.longhorn.io
volumes, err := client.Volumes(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list volumes.longhorn.io")
}
dir = GetLonghornVolumesDirectory(ns)
for _, volume := range volumes.Items {
b, err := yaml.Marshal(volume)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal volume %s", volume.Name)
}
key := filepath.Join(dir, volume.Name+".yaml")
final[key] = b
}
// collect replicas.longhorn.io
replicas, err := client.Replicas(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list replicas.longhorn.io")
}
dir = GetLonghornReplicasDirectory(ns)
for _, replica := range replicas.Items {
b, err := yaml.Marshal(replica)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal replica %s", replica.Name)
}
key := filepath.Join(dir, replica.Name+".yaml")
final[key] = b
}
// collect engines.longhorn.io
engines, err := client.Engines(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list engines.longhorn.io")
}
dir = GetLonghornEnginesDirectory(ns)
for _, engine := range engines.Items {
b, err := yaml.Marshal(engine)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal engine %s", engine.Name)
}
key := filepath.Join(dir, engine.Name+".yaml")
final[key] = b
}
// collect engineimages.longhorn.io
engineImages, err := client.EngineImages(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list engineimages.longhorn.io")
}
dir = GetLonghornEngineImagesDirectory(ns)
for _, engineImage := range engineImages.Items {
b, err := yaml.Marshal(engineImage)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal engineimage %s", engineImage.Name)
}
key := filepath.Join(dir, engineImage.Name+".yaml")
final[key] = b
}
// collect instancemanagers.longhorn.io
instanceManagers, err := client.InstanceManagers(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list instancemanagers.longhorn.io")
}
dir = GetLonghornInstanceManagersDirectory(ns)
for _, instanceManager := range instanceManagers.Items {
b, err := yaml.Marshal(instanceManager)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal instancemanager %s", instanceManager.Name)
}
key := filepath.Join(dir, instanceManager.Name+".yaml")
final[key] = b
}
// collect backingimagemanagers.longhorn.io
backingImageManagers, err := client.BackingImageManagers(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list backingimagemanagers.longhorn.io")
}
dir = GetLonghornBackingImageManagersDirectory(ns)
for _, backingImageManager := range backingImageManagers.Items {
b, err := yaml.Marshal(backingImageManager)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal backingimagemanager %s", backingImageManager.Name)
}
key := filepath.Join(dir, backingImageManager.Name+".yaml")
final[key] = b
}
// collect backingimages.longhorn.io
backingImages, err := client.BackingImages(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list backingimages.longhorn.io")
}
dir = GetLonghornBackingImagesDirectory(ns)
for _, backingImage := range backingImages.Items {
b, err := yaml.Marshal(backingImage)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal backingimage %s", backingImage.Name)
}
key := filepath.Join(dir, backingImage.Name+".yaml")
final[key] = b
}
// collect sharemanagers.longhorn.io
shareManagers, err := client.ShareManagers(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list sharemagemanagers.longhorn.io")
}
dir = GetLonghornShareManagersDirectory(ns)
for _, shareManager := range shareManagers.Items {
b, err := yaml.Marshal(shareManager)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal sharemanager %s", shareManager.Name)
}
key := filepath.Join(dir, shareManager.Name+".yaml")
final[key] = b
}
// collect settings
settings, err := client.Settings(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "list settings.longhorn.io")
}
settingsMap := map[string]string{}
for _, setting := range settings.Items {
settingsMap[setting.Name] = setting.Value
}
settingsKey := GetLonghornSettingsFile(ns)
settingsB, err := yaml.Marshal(settingsMap)
if err != nil {
return nil, errors.Wrap(err, "marshal longhorn settings")
}
final[settingsKey] = settingsB
return final, nil
}
func GetLonghornNodesDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/nodes", namespace)
}
return "longhorn/nodes"
}
func GetLonghornVolumesDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/volumes", namespace)
}
return "longhorn/volumes"
}
func GetLonghornReplicasDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/replicas", namespace)
}
return "longhorn/replicas"
}
func GetLonghornEnginesDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/engines", namespace)
}
return "longhorn/engines"
}
func GetLonghornEngineImagesDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/engineimages", namespace)
}
return "longhorn/engineimages"
}
func GetLonghornInstanceManagersDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/instancemanagers", namespace)
}
return "longhorn/instancemanagers"
}
func GetLonghornBackingImageManagersDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/backingimagemanagers", namespace)
}
return "longhorn/backingimagemanagers"
}
func GetLonghornBackingImagesDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/backingimages", namespace)
}
return "longhorn/backingimages"
}
func GetLonghornShareManagersDirectory(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/sharemanagers", namespace)
}
return "longhorn/sharemanagers"
}
func GetLonghornSettingsFile(namespace string) string {
if namespace != DefaultLonghornNamespace {
return fmt.Sprintf("longhorn/%s/settings.yaml", namespace)
}
return "longhorn/settings.yaml"
}