Consolidate timeouts for `linkerd check` (#2191)

Consolidate timeouts for `linkerd check`

- Moved the creation of contexts from inside the methods targeted by the
checks into a single place in the runCheck() and runCheckRPC() methods
where the context is built using a hard-coded timeout of 30 seconds.
- k8s' client-go doesn't allow passing along contexts, but it let's us
setting the Timeout manually.
- Reworded the description for the --wait option.

Signed-off-by: Alejandro Pedraza <alejandro@buoyant.io>
This commit is contained in:
Alejandro Pedraza 2019-02-05 11:38:30 -05:00 committed by GitHub
parent 66070c26f4
commit 2a7654ce78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 109 additions and 99 deletions

View File

@ -63,7 +63,7 @@ non-zero exit code.`,
cmd.PersistentFlags().StringVar(&options.versionOverride, "expected-version", options.versionOverride, "Overrides the version used when checking if Linkerd is running the latest version (mostly for testing)")
cmd.PersistentFlags().BoolVar(&options.preInstallOnly, "pre", options.preInstallOnly, "Only run pre-installation checks, to determine if the control plane can be installed")
cmd.PersistentFlags().BoolVar(&options.dataPlaneOnly, "proxy", options.dataPlaneOnly, "Only run data-plane checks, to determine if the data plane is healthy")
cmd.PersistentFlags().DurationVar(&options.wait, "wait", options.wait, "Retry and wait for some checks to succeed if they don't pass the first time")
cmd.PersistentFlags().DurationVar(&options.wait, "wait", options.wait, "Maximum allowed time for all tests to pass")
cmd.PersistentFlags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace to use for --proxy checks (default: all namespaces)")
cmd.PersistentFlags().BoolVar(&options.singleNamespace, "single-namespace", options.singleNamespace, "When running pre-installation checks (--pre), only check the permissions required to operate the control plane in a single namespace")

View File

@ -2,6 +2,7 @@ package cmd
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"testing"
@ -15,10 +16,10 @@ func TestCheckStatus(t *testing.T) {
[]healthcheck.CategoryID{},
&healthcheck.Options{},
)
hc.Add("category", "check1", "", func() error {
hc.Add("category", "check1", "", func(context.Context) error {
return nil
})
hc.Add("category", "check2", "http://linkerd.io/hint-url", func() error {
hc.Add("category", "check2", "http://linkerd.io/hint-url", func(context.Context) error {
return fmt.Errorf("This should contain instructions for fail")
})

View File

@ -1,9 +1,11 @@
package cmd
import (
"context"
"fmt"
"io"
"os"
"time"
"github.com/linkerd/linkerd2/controller/api/public"
pb "github.com/linkerd/linkerd2/controller/gen/public"
@ -65,7 +67,9 @@ func configureAndRunVersion(
os.Exit(1)
}
serverVersion, err := healthcheck.GetServerVersion(client)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
serverVersion, err := healthcheck.GetServerVersion(ctx, client)
if err != nil {
serverVersion = defaultVersionString
}

View File

@ -102,8 +102,8 @@ const (
)
var (
maxRetries = 60
retryWindow = 5 * time.Second
requestTimeout = 30 * time.Second
clusterZoneSuffix = []string{"svc", "cluster", "local"}
)
@ -130,12 +130,12 @@ type checker struct {
// check is the function that's called to execute the check; if the function
// returns an error, the check fails
check func() error
check func(context.Context) error
// checkRPC is an alternative to check that can be used to perform a remote
// check using the SelfCheck gRPC endpoint; check status is based on the value
// of the gRPC response
checkRPC func() (*healthcheckPb.SelfCheckResponse, error)
checkRPC func(context.Context) (*healthcheckPb.SelfCheckResponse, error)
}
// CheckResult encapsulates a check's identifying information and output
@ -222,8 +222,15 @@ func (hc *HealthChecker) allCategories() []category {
description: "can initialize the client",
hintURL: "https://linkerd.io/2/faq/#k8s-api",
fatal: true,
check: func() (err error) {
check: func(context.Context) (err error) {
hc.kubeAPI, err = k8s.NewAPI(hc.KubeConfig, hc.KubeContext)
if err != nil {
return
}
// k8s' client-go doesn't support injecting context
// https://github.com/kubernetes/kubernetes/issues/46503
// but we can set the timeout manually
hc.kubeAPI.Timeout = requestTimeout
return
},
},
@ -231,12 +238,12 @@ func (hc *HealthChecker) allCategories() []category {
description: "can query the Kubernetes API",
hintURL: "https://linkerd.io/2/faq/#k8s-api",
fatal: true,
check: func() (err error) {
check: func(ctx context.Context) (err error) {
hc.httpClient, err = hc.kubeAPI.NewClient()
if err != nil {
return
}
hc.kubeVersion, err = hc.kubeAPI.GetVersionInfo(hc.httpClient)
hc.kubeVersion, err = hc.kubeAPI.GetVersionInfo(ctx, hc.httpClient)
return
},
},
@ -248,7 +255,7 @@ func (hc *HealthChecker) allCategories() []category {
{
description: "is running the minimum Kubernetes API version",
hintURL: "https://linkerd.io/2/faq/#k8s-version",
check: func() error {
check: func(context.Context) error {
return hc.kubeAPI.CheckVersion(hc.kubeVersion)
},
},
@ -260,35 +267,35 @@ func (hc *HealthChecker) allCategories() []category {
{
description: "control plane namespace does not already exist",
hintURL: "https://linkerd.io/2/faq/#pre-ns",
check: func() error {
return hc.checkNamespace(hc.ControlPlaneNamespace, false)
check: func(ctx context.Context) error {
return hc.checkNamespace(ctx, hc.ControlPlaneNamespace, false)
},
},
{
description: "can create Namespaces",
hintURL: "https://linkerd.io/2/faq/#pre-k8s-cluster-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate("", "", "v1", "Namespace")
},
},
{
description: "can create ClusterRoles",
hintURL: "https://linkerd.io/2/faq/#pre-k8s-cluster-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate("", "rbac.authorization.k8s.io", "v1beta1", "ClusterRole")
},
},
{
description: "can create ClusterRoleBindings",
hintURL: "https://linkerd.io/2/faq/#pre-k8s-cluster-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate("", "rbac.authorization.k8s.io", "v1beta1", "ClusterRoleBinding")
},
},
{
description: "can create CustomResourceDefinitions",
hintURL: "https://linkerd.io/2/faq/#pre-k8s-cluster-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "apiextensions.k8s.io", "v1beta1", "CustomResourceDefinition")
},
},
@ -300,21 +307,21 @@ func (hc *HealthChecker) allCategories() []category {
{
description: "control plane namespace exists",
hintURL: "https://linkerd.io/2/faq/#pre-single-ns",
check: func() error {
return hc.checkNamespace(hc.ControlPlaneNamespace, true)
check: func(ctx context.Context) error {
return hc.checkNamespace(ctx, hc.ControlPlaneNamespace, true)
},
},
{
description: "can create Roles",
hintURL: "https://linkerd.io/2/faq/#pre-k8s-cluster-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "rbac.authorization.k8s.io", "v1beta1", "Role")
},
},
{
description: "can create RoleBindings",
hintURL: "https://linkerd.io/2/faq/#pre-k8s-cluster-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "rbac.authorization.k8s.io", "v1beta1", "RoleBinding")
},
},
@ -326,28 +333,28 @@ func (hc *HealthChecker) allCategories() []category {
{
description: "can create ServiceAccounts",
hintURL: "https://linkerd.io/2/faq/#pre-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "", "v1", "ServiceAccount")
},
},
{
description: "can create Services",
hintURL: "https://linkerd.io/2/faq/#pre-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "", "v1", "Service")
},
},
{
description: "can create Deployments",
hintURL: "https://linkerd.io/2/faq/#pre-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "extensions", "v1beta1", "Deployments")
},
},
{
description: "can create ConfigMaps",
hintURL: "https://linkerd.io/2/faq/#pre-k8s",
check: func() error {
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "", "v1", "ConfigMap")
},
},
@ -360,8 +367,8 @@ func (hc *HealthChecker) allCategories() []category {
description: "control plane namespace exists",
hintURL: "https://linkerd.io/2/faq/#l5d-existence-ns",
fatal: true,
check: func() error {
return hc.checkNamespace(hc.ControlPlaneNamespace, true)
check: func(ctx context.Context) error {
return hc.checkNamespace(ctx, hc.ControlPlaneNamespace, true)
},
},
{
@ -369,9 +376,9 @@ func (hc *HealthChecker) allCategories() []category {
hintURL: "https://linkerd.io/2/faq/#l5d-existence-controller",
retryDeadline: hc.RetryDeadline,
fatal: true,
check: func() error {
check: func(ctx context.Context) error {
var err error
hc.controlPlanePods, err = hc.kubeAPI.GetPodsByNamespace(hc.httpClient, hc.ControlPlaneNamespace)
hc.controlPlanePods, err = hc.kubeAPI.GetPodsByNamespace(ctx, hc.httpClient, hc.ControlPlaneNamespace)
if err != nil {
return err
}
@ -382,7 +389,7 @@ func (hc *HealthChecker) allCategories() []category {
description: "can initialize the client",
hintURL: "https://linkerd.io/2/faq/#l5d-existence-client",
fatal: true,
check: func() (err error) {
check: func(context.Context) (err error) {
if hc.APIAddr != "" {
hc.apiClient, err = public.NewInternalClient(hc.ControlPlaneNamespace, hc.APIAddr)
} else {
@ -396,8 +403,8 @@ func (hc *HealthChecker) allCategories() []category {
hintURL: "https://linkerd.io/2/faq/#l5d-existence-api",
retryDeadline: hc.RetryDeadline,
fatal: true,
check: func() (err error) {
hc.serverVersion, err = GetServerVersion(hc.apiClient)
check: func(ctx context.Context) (err error) {
hc.serverVersion, err = GetServerVersion(ctx, hc.apiClient)
return
},
},
@ -411,9 +418,9 @@ func (hc *HealthChecker) allCategories() []category {
hintURL: "https://linkerd.io/2/faq/#l5d-api-control-ready",
retryDeadline: hc.RetryDeadline,
fatal: true,
check: func() error {
check: func(ctx context.Context) error {
var err error
hc.controlPlanePods, err = hc.kubeAPI.GetPodsByNamespace(hc.httpClient, hc.ControlPlaneNamespace)
hc.controlPlanePods, err = hc.kubeAPI.GetPodsByNamespace(ctx, hc.httpClient, hc.ControlPlaneNamespace)
if err != nil {
return err
}
@ -425,9 +432,7 @@ func (hc *HealthChecker) allCategories() []category {
hintURL: "https://linkerd.io/2/faq/#l5d-api-control-api",
fatal: true,
retryDeadline: hc.RetryDeadline,
checkRPC: func() (*healthcheckPb.SelfCheckResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
checkRPC: func(ctx context.Context) (*healthcheckPb.SelfCheckResponse, error) {
return hc.apiClient.SelfCheck(ctx, &healthcheckPb.SelfCheckRequest{})
},
},
@ -440,7 +445,7 @@ func (hc *HealthChecker) allCategories() []category {
description: "no invalid service profiles",
hintURL: "https://linkerd.io/2/faq/#l5d-sp",
warning: true,
check: func() error {
check: func(context.Context) error {
return hc.validateServiceProfiles()
},
},
@ -452,7 +457,7 @@ func (hc *HealthChecker) allCategories() []category {
{
description: "can determine the latest version",
hintURL: "https://linkerd.io/2/faq/#l5d-version-latest",
check: func() (err error) {
check: func(ctx context.Context) (err error) {
if hc.VersionOverride != "" {
hc.latestVersions, err = version.NewChannels(hc.VersionOverride)
} else {
@ -472,7 +477,7 @@ func (hc *HealthChecker) allCategories() []category {
}
}
}
hc.latestVersions, err = version.GetLatestVersions(uuid, "cli")
hc.latestVersions, err = version.GetLatestVersions(ctx, uuid, "cli")
}
return
},
@ -481,7 +486,7 @@ func (hc *HealthChecker) allCategories() []category {
description: "cli is up-to-date",
hintURL: "https://linkerd.io/2/faq/#l5d-version-cli",
warning: true,
check: func() error {
check: func(context.Context) error {
return hc.latestVersions.Match(version.Version)
},
},
@ -494,7 +499,7 @@ func (hc *HealthChecker) allCategories() []category {
description: "control plane is up-to-date",
hintURL: "https://linkerd.io/2/faq/#l5d-version-control",
warning: true,
check: func() error {
check: func(context.Context) error {
return hc.latestVersions.Match(hc.serverVersion)
},
},
@ -502,7 +507,7 @@ func (hc *HealthChecker) allCategories() []category {
description: "control plane and cli versions match",
hintURL: "https://linkerd.io/2/faq/#l5d-version-control",
warning: true,
check: func() error {
check: func(context.Context) error {
if hc.serverVersion != version.Version {
return fmt.Errorf("control plane running %s but cli running %s", hc.serverVersion, version.Version)
}
@ -518,8 +523,8 @@ func (hc *HealthChecker) allCategories() []category {
description: "data plane namespace exists",
hintURL: "https://linkerd.io/2/faq/#l5d-data-plane-exists",
fatal: true,
check: func() error {
return hc.checkNamespace(hc.DataPlaneNamespace, true)
check: func(ctx context.Context) error {
return hc.checkNamespace(ctx, hc.DataPlaneNamespace, true)
},
},
{
@ -527,8 +532,8 @@ func (hc *HealthChecker) allCategories() []category {
hintURL: "https://linkerd.io/2/faq/#l5d-data-plane-ready",
retryDeadline: hc.RetryDeadline,
fatal: true,
check: func() error {
pods, err := hc.getDataPlanePods()
check: func(ctx context.Context) error {
pods, err := hc.getDataPlanePods(ctx)
if err != nil {
return err
}
@ -540,8 +545,8 @@ func (hc *HealthChecker) allCategories() []category {
description: "data plane proxy metrics are present in Prometheus",
hintURL: "https://linkerd.io/2/faq/#l5d-data-plane-prom",
retryDeadline: hc.RetryDeadline,
check: func() error {
pods, err := hc.getDataPlanePods()
check: func(ctx context.Context) error {
pods, err := hc.getDataPlanePods(ctx)
if err != nil {
return err
}
@ -553,8 +558,8 @@ func (hc *HealthChecker) allCategories() []category {
description: "data plane is up-to-date",
hintURL: "https://linkerd.io/2/faq/#l5d-data-plane-version",
warning: true,
check: func() error {
pods, err := hc.getDataPlanePods()
check: func(ctx context.Context) error {
pods, err := hc.getDataPlanePods(ctx)
if err != nil {
return err
}
@ -572,8 +577,8 @@ func (hc *HealthChecker) allCategories() []category {
description: "data plane and cli versions match",
hintURL: "https://linkerd.io/2/faq/#l5d-data-plane-cli-version",
warning: true,
check: func() error {
pods, err := hc.getDataPlanePods()
check: func(ctx context.Context) error {
pods, err := hc.getDataPlanePods(ctx)
if err != nil {
return err
}
@ -594,7 +599,7 @@ func (hc *HealthChecker) allCategories() []category {
// Add adds an arbitrary checker. This should only be used for testing. For
// production code, pass in the desired set of checks when calling
// NewHeathChecker.
func (hc *HealthChecker) Add(categoryID CategoryID, description string, hintURL string, check func() error) {
func (hc *HealthChecker) Add(categoryID CategoryID, description string, hintURL string, check func(context.Context) error) {
hc.addCategory(
category{
id: categoryID,
@ -656,7 +661,9 @@ func (hc *HealthChecker) RunChecks(observer checkObserver) bool {
func (hc *HealthChecker) runCheck(categoryID CategoryID, c *checker, observer checkObserver) bool {
for {
err := c.check()
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
err := c.check(ctx)
checkResult := &CheckResult{
Category: categoryID,
Description: c.description,
@ -681,7 +688,9 @@ func (hc *HealthChecker) runCheck(categoryID CategoryID, c *checker, observer ch
}
func (hc *HealthChecker) runCheckRPC(categoryID CategoryID, c *checker, observer checkObserver) bool {
checkRsp, err := c.checkRPC()
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
checkRsp, err := c.checkRPC(ctx)
observer(&CheckResult{
Category: categoryID,
Description: c.description,
@ -719,8 +728,8 @@ func (hc *HealthChecker) PublicAPIClient() pb.ApiClient {
return hc.apiClient
}
func (hc *HealthChecker) checkNamespace(namespace string, shouldExist bool) error {
exists, err := hc.kubeAPI.NamespaceExists(hc.httpClient, namespace)
func (hc *HealthChecker) checkNamespace(ctx context.Context, namespace string, shouldExist bool) error {
exists, err := hc.kubeAPI.NamespaceExists(ctx, hc.httpClient, namespace)
if err != nil {
return err
}
@ -733,7 +742,7 @@ func (hc *HealthChecker) checkNamespace(namespace string, shouldExist bool) erro
return nil
}
func (hc *HealthChecker) getDataPlanePods() ([]*pb.Pod, error) {
func (hc *HealthChecker) getDataPlanePods(ctx context.Context) ([]*pb.Pod, error) {
req := &pb.ListPodsRequest{}
if hc.DataPlaneNamespace != "" {
req.Selector = &pb.ResourceSelection{
@ -743,7 +752,7 @@ func (hc *HealthChecker) getDataPlanePods() ([]*pb.Pod, error) {
}
}
resp, err := hc.apiClient.ListPods(context.Background(), req)
resp, err := hc.apiClient.ListPods(ctx, req)
if err != nil {
return nil, err
}

View File

@ -16,14 +16,14 @@ import (
)
func TestHealthChecker(t *testing.T) {
nullObserver := func(_ *CheckResult) {}
nullObserver := func(*CheckResult) {}
passingCheck1 := category{
id: "cat1",
checkers: []checker{
checker{
description: "desc1",
check: func() error {
check: func(context.Context) error {
return nil
},
retryDeadline: time.Time{},
@ -36,7 +36,7 @@ func TestHealthChecker(t *testing.T) {
checkers: []checker{
checker{
description: "desc2",
check: func() error {
check: func(context.Context) error {
return nil
},
retryDeadline: time.Time{},
@ -49,7 +49,7 @@ func TestHealthChecker(t *testing.T) {
checkers: []checker{
checker{
description: "desc3",
check: func() error {
check: func(context.Context) error {
return fmt.Errorf("error")
},
retryDeadline: time.Time{},
@ -74,7 +74,7 @@ func TestHealthChecker(t *testing.T) {
checkers: []checker{
checker{
description: "desc4",
checkRPC: func() (*healthcheckPb.SelfCheckResponse, error) {
checkRPC: func(context.Context) (*healthcheckPb.SelfCheckResponse, error) {
return passingRPCClient.SelfCheck(context.Background(),
&healthcheckPb.SelfCheckRequest{})
},
@ -101,7 +101,7 @@ func TestHealthChecker(t *testing.T) {
checkers: []checker{
checker{
description: "desc5",
checkRPC: func() (*healthcheckPb.SelfCheckResponse, error) {
checkRPC: func(context.Context) (*healthcheckPb.SelfCheckResponse, error) {
return failingRPCClient.SelfCheck(context.Background(),
&healthcheckPb.SelfCheckRequest{})
},
@ -116,7 +116,7 @@ func TestHealthChecker(t *testing.T) {
checker{
description: "desc6",
fatal: true,
check: func() error {
check: func(context.Context) error {
return fmt.Errorf("fatal")
},
retryDeadline: time.Time{},
@ -249,7 +249,7 @@ func TestHealthChecker(t *testing.T) {
checker{
description: "desc7",
retryDeadline: time.Now().Add(100 * time.Second),
check: func() error {
check: func(context.Context) error {
if returnError {
returnError = false
return fmt.Errorf("retry")

View File

@ -2,16 +2,12 @@ package healthcheck
import (
"context"
"time"
pb "github.com/linkerd/linkerd2/controller/gen/public"
)
// GetServerVersion returns the Linkerd Public API server version
func GetServerVersion(apiClient pb.ApiClient) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
func GetServerVersion(ctx context.Context, apiClient pb.ApiClient) (string, error) {
rsp, err := apiClient.Version(ctx, &pb.Empty{})
if err != nil {
return "", err

View File

@ -1,8 +1,10 @@
package healthcheck
import (
"context"
"errors"
"testing"
"time"
"github.com/linkerd/linkerd2/controller/api/public"
pb "github.com/linkerd/linkerd2/controller/gen/public"
@ -24,7 +26,9 @@ func TestGetServerVersion(t *testing.T) {
ReleaseVersion: expectedServerVersion,
}
version, err := GetServerVersion(mockClient)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
version, err := GetServerVersion(ctx, mockClient)
if err != nil {
t.Fatalf("GetServerVersion returned unexpected error: %s", err)
}
@ -39,7 +43,9 @@ func TestGetServerVersion(t *testing.T) {
mockClient := &public.MockAPIClient{}
mockClient.ErrorToReturn = errors.New("expected")
_, err := GetServerVersion(mockClient)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err := GetServerVersion(ctx, mockClient)
if err != mockClient.ErrorToReturn {
t.Fatalf("GetServerVersion returned unexpected error: %s", err)
}

View File

@ -7,7 +7,6 @@ import (
"io/ioutil"
"net/http"
"net/url"
"time"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/version"
@ -38,10 +37,7 @@ func (kubeAPI *KubernetesAPI) NewClient() (*http.Client, error) {
}
// GetVersionInfo returns version.Info for the Kubernetes cluster.
func (kubeAPI *KubernetesAPI) GetVersionInfo(client *http.Client) (*version.Info, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
func (kubeAPI *KubernetesAPI) GetVersionInfo(ctx context.Context, client *http.Client) (*version.Info, error) {
rsp, err := kubeAPI.getRequest(ctx, client, "/version")
if err != nil {
return nil, err
@ -80,10 +76,7 @@ func (kubeAPI *KubernetesAPI) CheckVersion(versionInfo *version.Info) error {
}
// NamespaceExists validates whether a given namespace exists.
func (kubeAPI *KubernetesAPI) NamespaceExists(client *http.Client, namespace string) (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
func (kubeAPI *KubernetesAPI) NamespaceExists(ctx context.Context, client *http.Client, namespace string) (bool, error) {
rsp, err := kubeAPI.getRequest(ctx, client, "/api/v1/namespaces/"+namespace)
if err != nil {
return false, err
@ -98,14 +91,11 @@ func (kubeAPI *KubernetesAPI) NamespaceExists(client *http.Client, namespace str
}
// GetPodsByNamespace returns all pods in a given namespace
func (kubeAPI *KubernetesAPI) GetPodsByNamespace(client *http.Client, namespace string) ([]v1.Pod, error) {
return kubeAPI.getPods(client, "/api/v1/namespaces/"+namespace+"/pods")
func (kubeAPI *KubernetesAPI) GetPodsByNamespace(ctx context.Context, client *http.Client, namespace string) ([]v1.Pod, error) {
return kubeAPI.getPods(ctx, client, "/api/v1/namespaces/"+namespace+"/pods")
}
func (kubeAPI *KubernetesAPI) getPods(client *http.Client, path string) ([]v1.Pod, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
func (kubeAPI *KubernetesAPI) getPods(ctx context.Context, client *http.Client, path string) ([]v1.Pod, error) {
rsp, err := kubeAPI.getRequest(ctx, client, path)
if err != nil {
return nil, err

View File

@ -1,6 +1,7 @@
package k8s
import (
"context"
"fmt"
"io/ioutil"
"net"
@ -8,6 +9,7 @@ import (
"net/url"
"os"
"strings"
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
@ -56,7 +58,9 @@ func NewPortForward(
return nil, err
}
pods, err := kubeAPI.GetPodsByNamespace(client, namespace)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
pods, err := kubeAPI.GetPodsByNamespace(ctx, client, namespace)
if err != nil {
return nil, err
}

View File

@ -7,7 +7,6 @@ import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
// Channels provides an interface to interact with a set of release channels.
@ -59,20 +58,17 @@ func (c Channels) Match(actualVersion string) error {
// GetLatestVersions performs an online request to check for the latest Linkerd
// release channels.
func GetLatestVersions(uuid string, source string) (Channels, error) {
func GetLatestVersions(ctx context.Context, uuid string, source string) (Channels, error) {
url := fmt.Sprintf(versionCheckURL, Version, uuid, source)
return getLatestVersions(http.DefaultClient, url, uuid, source)
return getLatestVersions(ctx, http.DefaultClient, url, uuid, source)
}
func getLatestVersions(client *http.Client, url string, uuid string, source string) (Channels, error) {
func getLatestVersions(ctx context.Context, client *http.Client, url string, uuid string, source string) (Channels, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return Channels{}, err
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
rsp, err := client.Do(req.WithContext(ctx))
if err != nil {
return Channels{}, err

View File

@ -1,12 +1,14 @@
package version
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestGetLatestVersions(t *testing.T) {
@ -68,7 +70,9 @@ func TestGetLatestVersions(t *testing.T) {
)
defer ts.Close()
latest, err := getLatestVersions(ts.Client(), ts.URL, "uuid", "source")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
latest, err := getLatestVersions(ctx, ts.Client(), ts.URL, "uuid", "source")
if (err == nil && tc.err != nil) ||
(err != nil && tc.err == nil) ||
((err != nil && tc.err != nil) && (err.Error() != tc.err.Error())) {