diff --git a/config/crds/troubleshoot.sh_collectors.yaml b/config/crds/troubleshoot.sh_collectors.yaml index e44661509..02a85fa29 100644 --- a/config/crds/troubleshoot.sh_collectors.yaml +++ b/config/crds/troubleshoot.sh_collectors.yaml @@ -348,6 +348,8 @@ spec: type: object goldpinger: properties: + collectDelay: + type: string collectorName: type: string exclude: @@ -374,6 +376,8 @@ spec: serviceAccountName: type: string type: object + serviceAccountName: + type: string type: object helm: properties: diff --git a/config/crds/troubleshoot.sh_preflights.yaml b/config/crds/troubleshoot.sh_preflights.yaml index 5bb6ac940..38e900de7 100644 --- a/config/crds/troubleshoot.sh_preflights.yaml +++ b/config/crds/troubleshoot.sh_preflights.yaml @@ -2077,6 +2077,8 @@ spec: type: object goldpinger: properties: + collectDelay: + type: string collectorName: type: string exclude: @@ -2103,6 +2105,8 @@ spec: serviceAccountName: type: string type: object + serviceAccountName: + type: string type: object helm: properties: diff --git a/config/crds/troubleshoot.sh_supportbundles.yaml b/config/crds/troubleshoot.sh_supportbundles.yaml index fe20a28de..9c77af9d6 100644 --- a/config/crds/troubleshoot.sh_supportbundles.yaml +++ b/config/crds/troubleshoot.sh_supportbundles.yaml @@ -2108,6 +2108,8 @@ spec: type: object goldpinger: properties: + collectDelay: + type: string collectorName: type: string exclude: @@ -2134,6 +2136,8 @@ spec: serviceAccountName: type: string type: object + serviceAccountName: + type: string type: object helm: properties: diff --git a/pkg/analyze/goldpinger_test.go b/pkg/analyze/goldpinger_test.go index ad80adb02..c004475bd 100644 --- a/pkg/analyze/goldpinger_test.go +++ b/pkg/analyze/goldpinger_test.go @@ -96,6 +96,18 @@ func TestAnalyzeGoldpinger_podPingsAnalysis(t *testing.T) { }, }, }, + { + name: "no ping errors for one pod", + cao: caoFixture(t, "goldpinger/checkall-one-pod.json"), + want: []*AnalyzeResult{ + { + Title: "Pings to \"gp-goldpinger-xn9rg\" pod succeeded", + Message: "Pings to \"gp-goldpinger-xn9rg\" pod from all other pods in the cluster succeeded", + IconKey: "kubernetes", + IsPass: true, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/apis/troubleshoot/v1beta2/collector_shared.go b/pkg/apis/troubleshoot/v1beta2/collector_shared.go index 40a97c1dc..04181d7ac 100644 --- a/pkg/apis/troubleshoot/v1beta2/collector_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/collector_shared.go @@ -277,9 +277,11 @@ type Helm struct { } type Goldpinger struct { - CollectorMeta `json:",inline" yaml:",inline"` - Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` - PodLaunchOptions *PodLaunchOptions `json:"podLaunchOptions,omitempty" yaml:"podLaunchOptions,omitempty"` + CollectorMeta `json:",inline" yaml:",inline"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty" yaml:"serviceAccountName,omitempty"` + CollectDelay string `json:"collectDelay,omitempty" yaml:"collectDelay,omitempty"` + PodLaunchOptions *PodLaunchOptions `json:"podLaunchOptions,omitempty" yaml:"podLaunchOptions,omitempty"` } type PodLaunchOptions struct { diff --git a/pkg/collect/goldpinger.go b/pkg/collect/goldpinger.go index 14a5a5cc8..c7142b949 100644 --- a/pkg/collect/goldpinger.go +++ b/pkg/collect/goldpinger.go @@ -14,11 +14,16 @@ import ( "github.com/replicatedhq/troubleshoot/internal/util" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/replicatedhq/troubleshoot/pkg/constants" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/klog/v2" + "k8s.io/utils/ptr" ) // Collect goldpinger results from goldpinger service running in a cluster @@ -47,9 +52,26 @@ func (c *CollectGoldpinger) Collect(progressChan chan<- interface{}) (CollectorR var results []byte var err error + // Check if we have goldpinger running in the cluster, if not, lets deploy it and collect results + namespace := "default" + if c.Collector.Namespace != "" { + namespace = c.Collector.Namespace + } + + url, resources, err := c.DiscoverOrCreateGoldpinger(namespace) + if err != nil { + klog.Errorf("Failed to ensure goldpinger is running: %v", err) + return nil, errors.Wrap(err, "failed to ensure goldpinger is running") + } + defer func() { + if err := c.cleanupResources(resources); err != nil { + klog.Errorf("Failed to cleanup resources: %v", err) + } + }() + if util.IsInCluster() { klog.V(2).Infof("Collector running in cluster, querying goldpinger endpoint straight away") - results, err = c.fetchCheckAllOutput() + results, err = c.fetchCheckAllOutput(url) if err != nil { errMsg := fmt.Sprintf("Failed to query goldpinger endpoint in cluster: %v", err) klog.V(2).Infof(errMsg) @@ -58,7 +80,7 @@ func (c *CollectGoldpinger) Collect(progressChan chan<- interface{}) (CollectorR } } else { klog.V(2).Infof("Launch pod to query goldpinger endpoint then collect results from pod logs") - results, err = c.runPodAndCollectGPResults(progressChan) + results, err = c.runPodAndCollectGPResults(url, progressChan) if err != nil { errMsg := fmt.Sprintf("Failed to run pod to collect goldpinger results: %v", err) klog.V(2).Infof(errMsg) @@ -71,12 +93,366 @@ func (c *CollectGoldpinger) Collect(progressChan chan<- interface{}) (CollectorR return output, err } -func (c *CollectGoldpinger) fetchCheckAllOutput() ([]byte, error) { +// cleanupResources collects all created resources for later deletion +// If creation of any resource fails, the already created resources +// will be deleted +type createdResources struct { + Role *rbacv1.Role + RoleBinding *rbacv1.RoleBinding + DaemonSet *appsv1.DaemonSet + ServiceAccnt *corev1.ServiceAccount + Service *corev1.Service +} + +func (c *CollectGoldpinger) cleanupResources(resources createdResources) error { + var errs []error + if resources.Service != nil { + if err := c.Client.CoreV1().Services(resources.Service.Namespace).Delete(c.Context, resources.Service.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to delete Service %s", resources.Service.Name)) + } + klog.V(2).Infof("%s Service deleted", resources.Service.Name) + } + + if resources.DaemonSet != nil { + if err := c.Client.AppsV1().DaemonSets(resources.DaemonSet.Namespace).Delete(c.Context, resources.DaemonSet.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to delete DaemonSet %s", resources.DaemonSet.Name)) + } + klog.V(2).Infof("%s DaemonSet deleted", resources.DaemonSet.Name) + } + + if resources.ServiceAccnt != nil { + if err := c.Client.CoreV1().ServiceAccounts(resources.ServiceAccnt.Namespace).Delete(c.Context, resources.ServiceAccnt.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to delete ServiceAccount %s", resources.ServiceAccnt.Name)) + } + klog.V(2).Infof("%s ServiceAccount deleted", resources.ServiceAccnt.Name) + } + + if resources.RoleBinding != nil { + if err := c.Client.RbacV1().RoleBindings(resources.RoleBinding.Namespace).Delete(c.Context, resources.RoleBinding.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to delete RoleBinding %s", resources.RoleBinding.Name)) + } + klog.V(2).Infof("%s RoleBinding deleted", resources.RoleBinding.Name) + } + + if resources.Role != nil { + if err := c.Client.RbacV1().Roles(resources.Role.Namespace).Delete(c.Context, resources.Role.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to delete Role %s", resources.Role.Name)) + } + klog.V(2).Infof("%s Role deleted", resources.Role.Name) + } + + if len(errs) > 0 { + return errors.Errorf("failed to cleanup resources: %v", errs) + } + + return nil +} + +func getUrlFromService(svc *corev1.Service) string { + return fmt.Sprintf("http://%s.%s.svc.cluster.local:%d/check_all", svc.Name, svc.Namespace, svc.Spec.Ports[0].Port) +} + +func parseCollectDelay(delay, defaultDelay string) (time.Duration, error) { + if delay == "" { + delay = defaultDelay + } + return time.ParseDuration(delay) +} + +func (c *CollectGoldpinger) DiscoverOrCreateGoldpinger(ns string) (string, createdResources, error) { + // Check if goldpinger is running in the cluster by searching for goldpinger's service + ret := createdResources{} + gpSvc, err := c.getGoldpingerService(ns) + if err != nil { + return "", ret, errors.Wrap(err, "failed to get goldpinger service") + } + + if gpSvc != nil { + klog.V(2).Infof("Goldpinger service already exists") + // By default, no delay needed if goldpinger service already exists + delay, err := parseCollectDelay(c.Collector.CollectDelay, "0s") + if err != nil { + return "", ret, errors.Wrap(err, "failed to parse duration") + } + time.Sleep(delay) + return getUrlFromService(gpSvc), ret, nil + } + + // If we deploy GP, we need to wait for it to ping pods + // Defaults to REFRESH_INTERVAL + CHECK_ALL_TIMEOUT + delay, err := parseCollectDelay(c.Collector.CollectDelay, "6s") + if err != nil { + return "", ret, errors.Wrap(err, "failed to parse duration") + } + + serviceAccountName := c.Collector.ServiceAccountName + if serviceAccountName == "" { + serviceAccountName = "ts-goldpinger-serviceaccount" + + svcAcc, err := c.createGoldpingerServiceAccount(ns, serviceAccountName) + if err != nil { + return "", ret, errors.Wrap(err, "failed to create goldpinger service account") + } + ret.ServiceAccnt = svcAcc + klog.V(2).Infof("%s ServiceAccount created", svcAcc.Name) + + r, err := c.createGoldpingerRole(ns) + if err != nil { + return "", ret, errors.Wrap(err, "failed to create goldpinger role") + } + ret.Role = r + klog.V(2).Infof("%s Role created", r.Name) + + rb, err := c.createGoldpingerRoleBinding(ns) + if err != nil { + return "", ret, errors.Wrap(err, "failed to create goldpinger role binding") + } + ret.RoleBinding = rb + klog.V(2).Infof("%s RoleBinding created", rb.Name) + } else { + if err := checkForExistingServiceAccount(c.Context, c.Client, ns, serviceAccountName); err != nil { + return "", ret, err + } + } + + ds, err := c.createGoldpingerDaemonSet(ns, serviceAccountName) + if err != nil { + return "", ret, errors.Wrap(err, "failed to create goldpinger daemonset") + } + ret.DaemonSet = ds + klog.V(2).Infof("%s DaemonSet created", ds.Name) + + // block till DaemonSet has right number of scheduled Pods + timeoutCtx, cancel := context.WithTimeout(c.Context, defaultTimeout) + defer cancel() + + err = waitForDaemonSetPods(timeoutCtx, c.Client, ds) + if err != nil { + return "", ret, errors.Wrapf(err, "failed to wait for %s DaemonSet pods", ds.Name) + } + klog.V(2).Infof("DaemonSet %s has desired number of pods", ds.Name) + + time.Sleep(delay) + + svc, err := c.createGoldpingerService(ns) + if err != nil { + return "", ret, errors.Wrap(err, "failed to create goldpinger service") + } + klog.V(2).Infof("%s Service created", svc.Name) + ret.Service = svc + + return getUrlFromService(svc), ret, nil +} + +func (c *CollectGoldpinger) createGoldpingerServiceAccount(ns, name string) (*corev1.ServiceAccount, error) { + svcAcc := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + return c.Client.CoreV1().ServiceAccounts(ns).Create(c.Context, svcAcc, metav1.CreateOptions{}) +} + +func (c *CollectGoldpinger) createGoldpingerRole(ns string) (*rbacv1.Role, error) { + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ts-goldpinger-role", + Namespace: ns, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, + } + + return c.Client.RbacV1().Roles(ns).Create(c.Context, role, metav1.CreateOptions{}) +} + +func (c *CollectGoldpinger) createGoldpingerRoleBinding(ns string) (*rbacv1.RoleBinding, error) { + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ts-goldpinger-rolebinding", + Namespace: ns, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: "ts-goldpinger-serviceaccount", + Namespace: ns, + }, + }, + RoleRef: rbacv1.RoleRef{ + Kind: "Role", + Name: "ts-goldpinger-role", + APIGroup: "rbac.authorization.k8s.io", + }, + } + + return c.Client.RbacV1().RoleBindings(ns).Create(c.Context, roleBinding, metav1.CreateOptions{}) +} + +func (c *CollectGoldpinger) createGoldpingerDaemonSet(ns, svcAccName string) (*appsv1.DaemonSet, error) { + ds := &appsv1.DaemonSet{} + + ds.ObjectMeta = metav1.ObjectMeta{ + Name: "ts-goldpinger", + Namespace: ns, + Labels: gpNameLabels(), + } + + ds.Spec = appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: gpNameLabels(), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: gpNameLabels(), + Namespace: ns, + }, + Spec: corev1.PodSpec{ + PriorityClassName: "system-node-critical", + ServiceAccountName: svcAccName, + Containers: []corev1.Container{ + { + Name: "goldpinger-daemon", + Image: "bloomberg/goldpinger:3.10.1", + ImagePullPolicy: corev1.PullIfNotPresent, + Env: []corev1.EnvVar{ + { + Name: "HOSTNAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "REFRESH_INTERVAL", + Value: "3", // Refresh interval in seconds. Its not a duration, its a number + }, + { + Name: "CHECK_ALL_TIMEOUT", + Value: "3s", + }, + { + Name: "HOST", + Value: "0.0.0.0", + }, + { + Name: "PORT", + Value: "8080", + }, + { + Name: "LABEL_SELECTOR", + Value: gpNameLabelSelector(), + }, + }, + SecurityContext: &corev1.SecurityContext{ + AllowPrivilegeEscalation: ptr.To(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + ReadOnlyRootFilesystem: ptr.To(true), + RunAsNonRoot: ptr.To(true), + }, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 8080, + Protocol: corev1.ProtocolTCP, + }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromString("http"), + }, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromString("http"), + }, + }, + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("64Mi"), + }, + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{ + FSGroup: ptr.To(int64(2000)), + RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(int64(1000)), + SeccompProfile: &corev1.SeccompProfile{ + Type: "RuntimeDefault", + }, + }, + }, + }, + } + + return c.Client.AppsV1().DaemonSets(ns).Create(c.Context, ds, metav1.CreateOptions{}) +} + +func (c *CollectGoldpinger) createGoldpingerService(ns string) (*corev1.Service, error) { + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ts-goldpinger", + Namespace: ns, + Labels: gpNameLabels(), + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Port: 80, + TargetPort: intstr.FromInt(8080), + Protocol: corev1.ProtocolTCP, + Name: "http", + }, + }, + Selector: gpNameLabels(), + }, + } + + return c.Client.CoreV1().Services(ns).Create(c.Context, svc, metav1.CreateOptions{}) +} + +func (c *CollectGoldpinger) getGoldpingerService(ns string) (*corev1.Service, error) { + svcs, err := c.Client.CoreV1().Services(ns).List(c.Context, metav1.ListOptions{ + LabelSelector: gpNameLabelSelector(), + }) + if err != nil { + return nil, errors.Wrap(err, "failed to list goldpinger services") + } + + if len(svcs.Items) == 0 { + return nil, nil + } + + return &svcs.Items[0], nil +} + +func (c *CollectGoldpinger) fetchCheckAllOutput(url string) ([]byte, error) { client := &http.Client{ Timeout: time.Minute, // Long enough timeout } - req, err := http.NewRequestWithContext(c.Context, "GET", c.endpoint(), nil) + req, err := http.NewRequestWithContext(c.Context, "GET", url, nil) if err != nil { return nil, err } @@ -99,9 +475,7 @@ func (c *CollectGoldpinger) fetchCheckAllOutput() ([]byte, error) { return body, nil } -func (c *CollectGoldpinger) runPodAndCollectGPResults(progressChan chan<- interface{}) ([]byte, error) { - rest.InClusterConfig() - +func (c *CollectGoldpinger) runPodAndCollectGPResults(url string, progressChan chan<- interface{}) ([]byte, error) { namespace := "default" serviceAccountName := "" image := constants.GP_DEFAULT_IMAGE @@ -144,7 +518,7 @@ func (c *CollectGoldpinger) runPodAndCollectGPResults(progressChan chan<- interf ImagePullPolicy: corev1.PullIfNotPresent, Name: collectorContainerName, Command: []string{"wget"}, - Args: []string{"-q", "-O-", c.endpoint()}, + Args: []string{"-q", "-O-", url}, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("50m"), @@ -162,7 +536,7 @@ func (c *CollectGoldpinger) runPodAndCollectGPResults(progressChan chan<- interf rbacErrors := c.GetRBACErrors() // Pass an empty bundle path since we don't need to save the results - runPodCollector := &CollectRunPod{runPodSpec, "", c.Namespace, c.ClientConfig, c.Client, c.Context, rbacErrors} + runPodCollector := &CollectRunPod{runPodSpec, "", c.Collector.Namespace, c.ClientConfig, c.Client, c.Context, rbacErrors} output, err := runPodCollector.Collect(progressChan) if err != nil { @@ -204,11 +578,12 @@ func (c *CollectGoldpinger) runPodAndCollectGPResults(progressChan chan<- interf return podLogs, nil } -func (c *CollectGoldpinger) endpoint() string { - namespace := c.Collector.Namespace - if namespace == "" { - namespace = constants.GP_DEFAULT_NAMESPACE +func gpNameLabels() map[string]string { + return map[string]string{ + "app.kubernetes.io/name": "goldpinger", } +} - return fmt.Sprintf("http://goldpinger.%s.svc.cluster.local:80/check_all", namespace) +func gpNameLabelSelector() string { + return "app.kubernetes.io/name=goldpinger" } diff --git a/schemas/collector-troubleshoot-v1beta2.json b/schemas/collector-troubleshoot-v1beta2.json index 18c066ab3..21241d7fd 100644 --- a/schemas/collector-troubleshoot-v1beta2.json +++ b/schemas/collector-troubleshoot-v1beta2.json @@ -475,6 +475,9 @@ "goldpinger": { "type": "object", "properties": { + "collectDelay": { + "type": "string" + }, "collectorName": { "type": "string" }, @@ -514,6 +517,9 @@ "type": "string" } } + }, + "serviceAccountName": { + "type": "string" } } }, diff --git a/schemas/preflight-troubleshoot-v1beta2.json b/schemas/preflight-troubleshoot-v1beta2.json index 93e56b2f8..0555adce9 100644 --- a/schemas/preflight-troubleshoot-v1beta2.json +++ b/schemas/preflight-troubleshoot-v1beta2.json @@ -3139,6 +3139,9 @@ "goldpinger": { "type": "object", "properties": { + "collectDelay": { + "type": "string" + }, "collectorName": { "type": "string" }, @@ -3178,6 +3181,9 @@ "type": "string" } } + }, + "serviceAccountName": { + "type": "string" } } }, diff --git a/schemas/supportbundle-troubleshoot-v1beta2.json b/schemas/supportbundle-troubleshoot-v1beta2.json index 100a829ce..8c46f6b7c 100644 --- a/schemas/supportbundle-troubleshoot-v1beta2.json +++ b/schemas/supportbundle-troubleshoot-v1beta2.json @@ -3185,6 +3185,9 @@ "goldpinger": { "type": "object", "properties": { + "collectDelay": { + "type": "string" + }, "collectorName": { "type": "string" }, @@ -3224,6 +3227,9 @@ "type": "string" } } + }, + "serviceAccountName": { + "type": "string" } } }, diff --git a/test/e2e/support-bundle/goldpinger_collector_e2e_test.go b/test/e2e/support-bundle/goldpinger_collector_e2e_test.go index e73ca3fd4..47c63a714 100644 --- a/test/e2e/support-bundle/goldpinger_collector_e2e_test.go +++ b/test/e2e/support-bundle/goldpinger_collector_e2e_test.go @@ -10,19 +10,12 @@ import ( "path/filepath" "strings" "testing" - "time" - "github.com/replicatedhq/troubleshoot/internal/testutils" "github.com/replicatedhq/troubleshoot/pkg/convert" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/e2e-framework/klient/wait" - "sigs.k8s.io/e2e-framework/klient/wait/conditions" "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/features" - "sigs.k8s.io/e2e-framework/third_party/helm" ) var specTemplate = ` @@ -38,43 +31,13 @@ spec: exclude: true - goldpinger: namespace: $NAMESPACE + collectDelay: 20s analyzers: - goldpinger: {} ` func Test_GoldpingerCollector(t *testing.T) { - releaseName := "goldpinger" - feature := features.New("Goldpinger collector and analyser"). - Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { - cluster := getClusterFromContext(t, ctx, ClusterName) - - manager := helm.New(cluster.GetKubeconfig()) - err := manager.RunInstall( - helm.WithName(releaseName), - helm.WithNamespace(c.Namespace()), - helm.WithChart(testutils.TestFixtureFilePath(t, "charts/goldpinger-6.0.1.tgz")), - helm.WithWait(), - helm.WithTimeout("2m"), - ) - require.NoError(t, err) - - client, err := c.NewClient() - require.NoError(t, err) - - // Lets wait for the goldpinger pods to be running - ds := &appsv1.DaemonSet{ObjectMeta: metav1.ObjectMeta{Name: "goldpinger", Namespace: c.Namespace()}} - err = wait.For( - conditions.New(client.Resources()).DaemonSetReady(ds), - wait.WithTimeout(time.Second*30), - ) - require.NoError(t, err) - - // HACK: wait for goldpinger to do its thing - time.Sleep(time.Second * 30) - - return ctx - }). Assess("collect and analyse goldpinger pings", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { var out bytes.Buffer var stdErr bytes.Buffer @@ -107,8 +70,8 @@ func Test_GoldpingerCollector(t *testing.T) { // Check that we analysed collected goldpinger results. // We should expect a single analysis result for goldpinger. - assert.Equal(t, 1, len(analysisResults)) - assert.True(t, strings.HasPrefix(analysisResults[0].Name, "pings.to.goldpinger.")) + require.Equal(t, 1, len(analysisResults)) + assert.True(t, strings.HasPrefix(analysisResults[0].Name, "pings.to.ts.goldpinger.")) if t.Failed() { t.Logf("Analysis results: %s\n", analysisJSON) t.Logf("Stdout: %s\n", out.String()) @@ -117,14 +80,6 @@ func Test_GoldpingerCollector(t *testing.T) { } return ctx - }). - Teardown(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { - cluster := getClusterFromContext(t, ctx, ClusterName) - manager := helm.New(cluster.GetKubeconfig()) - err := manager.RunUninstall(helm.WithName(releaseName), helm.WithNamespace(c.Namespace())) - require.NoError(t, err) - return ctx - }). - Feature() + }).Feature() testenv.Test(t, feature) } diff --git a/testdata/charts/goldpinger-6.0.1.tgz b/testdata/charts/goldpinger-6.0.1.tgz deleted file mode 100644 index 10e44e5e0..000000000 Binary files a/testdata/charts/goldpinger-6.0.1.tgz and /dev/null differ diff --git a/testdata/goldpinger/checkall-one-pod.json b/testdata/goldpinger/checkall-one-pod.json new file mode 100644 index 000000000..0a55b7dbc --- /dev/null +++ b/testdata/goldpinger/checkall-one-pod.json @@ -0,0 +1,30 @@ +{ + "hosts": [ + { + "hostIP": "172.18.0.3", + "podIP": "10.42.0.17", + "podName": "gp-goldpinger-xn9rg" + } + ], + "responses": { + "gp-goldpinger-xn9rg": { + "HostIP": "172.18.0.3", + "OK": true, + "PodIP": "10.42.0.17", + "response": { + "podResults": { + "gp-goldpinger-xn9rg": { + "HostIP": "172.18.0.3", + "OK": true, + "PingTime": "2024-09-24T07:10:10.431Z", + "PodIP": "10.42.0.17", + "response": { + "boot_time": "2024-09-24T07:09:40.411Z" + }, + "status-code": 200 + } + } + } + } + } +} \ No newline at end of file