cli: Remove get cmd and relevant tests (#5202)

Fixes #5190

`linkerd get` is not used currently and works only for pods. This can be
removed instead as per the issue. This branch removes the command and
also the associated unit and integration tests.

Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>
This commit is contained in:
Tarun Pothulapati 2020-11-13 00:49:46 +05:30 committed by GitHub
parent 4ffb41ab44
commit e4c354985c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 0 additions and 462 deletions

View File

@ -1,102 +0,0 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
pb "github.com/linkerd/linkerd2/controller/gen/public"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/spf13/cobra"
)
type getOptions struct {
namespace string
allNamespaces bool
}
func newGetOptions() *getOptions {
return &getOptions{
namespace: defaultNamespace,
allNamespaces: false,
}
}
func newCmdGet() *cobra.Command {
options := newGetOptions()
cmd := &cobra.Command{
Use: "get [flags] pods",
Short: "Display one or many mesh resources",
Long: `Display one or many mesh resources.
Only pod resources (aka pods, po) are supported.`,
Example: ` # get all pods
linkerd get pods
# get pods from namespace linkerd
linkerd get pods --namespace linkerd`,
Args: cobra.ExactArgs(1),
ValidArgs: []string{k8s.Pod},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("please specify a resource type")
}
if len(args) > 1 {
return errors.New("please specify only one resource type")
}
friendlyName := args[0]
resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(friendlyName)
if err != nil || resourceType != k8s.Pod {
return fmt.Errorf("invalid resource type %s, valid types: %s", friendlyName, k8s.Pod)
}
podNames, err := getPods(checkPublicAPIClientOrExit(), options)
if err != nil {
return err
}
if len(podNames) == 0 {
fmt.Fprintln(os.Stderr, "No resources found.")
os.Exit(0)
}
for _, podName := range podNames {
fmt.Println(podName)
}
return nil
},
}
cmd.PersistentFlags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace of pods")
cmd.PersistentFlags().BoolVarP(&options.allNamespaces, "all-namespaces", "A", options.allNamespaces, "If present, returns pods across all namespaces, ignoring the \"--namespace\" flag")
return cmd
}
func getPods(apiClient pb.ApiClient, options *getOptions) ([]string, error) {
req := &pb.ListPodsRequest{}
if !options.allNamespaces {
req.Selector = &pb.ResourceSelection{
Resource: &pb.Resource{
Namespace: options.namespace,
},
}
}
resp, err := apiClient.ListPods(context.Background(), req)
if err != nil {
return nil, err
}
names := make([]string, 0)
for _, pod := range resp.GetPods() {
names = append(names, pod.Name)
}
return names, nil
}

View File

@ -1,70 +0,0 @@
package cmd
import (
"errors"
"testing"
"github.com/linkerd/linkerd2/controller/api/public"
pb "github.com/linkerd/linkerd2/controller/gen/public"
)
func TestGetPods(t *testing.T) {
t.Run("Returns names of existing pods if everything went ok", func(t *testing.T) {
mockClient := &public.MockAPIClient{}
pods := []*pb.Pod{
{Name: "pod-a"},
{Name: "pod-b"},
{Name: "pod-c"},
}
expectedPodNames := []string{
"pod-a",
"pod-b",
"pod-c",
}
response := &pb.ListPodsResponse{
Pods: pods,
}
mockClient.ListPodsResponseToReturn = response
actualPodNames, err := getPods(mockClient, newGetOptions())
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
for i, actualName := range actualPodNames {
expectedName := expectedPodNames[i]
if expectedName != actualName {
t.Fatalf("Expected %dth element on %v to be [%s], but was [%s]", i, actualPodNames, expectedName, actualName)
}
}
})
t.Run("Returns empty list if no pods found", func(t *testing.T) {
mockClient := &public.MockAPIClient{}
mockClient.ListPodsResponseToReturn = &pb.ListPodsResponse{
Pods: []*pb.Pod{},
}
actualPodNames, err := getPods(mockClient, newGetOptions())
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if len(actualPodNames) != 0 {
t.Fatalf("Expecting no pod names, got %v", actualPodNames)
}
})
t.Run("Returns error if can't find pods in API", func(t *testing.T) {
mockClient := &public.MockAPIClient{}
mockClient.ErrorToReturn = errors.New("expected")
_, err := getPods(mockClient, newGetOptions())
if err == nil {
t.Fatalf("Expecting error, got noting")
}
})
}

View File

@ -113,7 +113,6 @@ func init() {
RootCmd.AddCommand(newCmdDoc())
RootCmd.AddCommand(newCmdEdges())
RootCmd.AddCommand(newCmdEndpoints())
RootCmd.AddCommand(newCmdGet())
RootCmd.AddCommand(newCmdInject())
RootCmd.AddCommand(newCmdInstall())
RootCmd.AddCommand(newCmdInstallCNIPlugin())

View File

@ -1,195 +0,0 @@
package get
import (
"context"
"fmt"
"io/ioutil"
"os"
"reflect"
"regexp"
"sort"
"strings"
"testing"
"github.com/linkerd/linkerd2/testutil"
)
//////////////////////
/// TEST SETUP ///
//////////////////////
var TestHelper *testutil.TestHelper
func TestMain(m *testing.M) {
TestHelper = testutil.NewTestHelper()
os.Exit(testutil.Run(m, TestHelper))
}
var (
deployReplicas = map[string]int{
"cli-get-test-d1": 2,
"cli-get-test-d2": 1,
"cli-get-test-not-injected-d1": 2,
"cli-get-test-not-injected-d2": 1,
}
linkerdPods = map[string]int{
"linkerd-controller": 1,
"linkerd-destination": 1,
"linkerd-grafana": 1,
"linkerd-identity": 1,
"linkerd-prometheus": 1,
"linkerd-proxy-injector": 1,
"linkerd-sp-validator": 1,
"linkerd-tap": 1,
"linkerd-web": 1,
}
)
//////////////////////
/// TEST EXECUTION ///
//////////////////////
func TestCliGet(t *testing.T) {
out, err := TestHelper.LinkerdRun("inject", "testdata/to_be_injected_application.yaml")
if err != nil {
testutil.AnnotatedFatal(t, "unexpected error", err)
}
ctx := context.Background()
TestHelper.WithDataPlaneNamespace(ctx, "get-test", map[string]string{}, t, func(t *testing.T, prefixedNs string) {
out, err = TestHelper.KubectlApply(out, prefixedNs)
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
bytes, err := ioutil.ReadFile("testdata/not_to_be_injected_application.yaml")
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v", err)
}
out, err = TestHelper.KubectlApply(string(bytes), prefixedNs)
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
// wait for pods to start
for deploy, replicas := range deployReplicas {
if err := TestHelper.CheckPods(ctx, prefixedNs, deploy, replicas); err != nil {
if rce, ok := err.(*testutil.RestartCountError); ok {
testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
} else {
testutil.AnnotatedError(t, "CheckPods timed-out", err)
}
}
}
t.Run("get pods from --all-namespaces", func(t *testing.T) {
out, err = TestHelper.LinkerdRun("get", "pods", "--all-namespaces")
if err != nil {
testutil.AnnotatedFatal(t, "unexpected error", err)
}
err := checkPodOutput(out, deployReplicas, "", prefixedNs)
if err != nil {
testutil.AnnotatedFatalf(t, "pod output check failed", "pod output check failed:\n%s\nCommand output:\n%s", err, out)
}
})
t.Run("get pods from the linkerd namespace", func(t *testing.T) {
out, err = TestHelper.LinkerdRun("get", "pods", "-n", TestHelper.GetLinkerdNamespace())
if err != nil {
testutil.AnnotatedFatal(t, "unexpected error", err)
}
err := checkPodOutput(out, linkerdPods, "linkerd-heartbeat", TestHelper.GetLinkerdNamespace())
if err != nil {
testutil.AnnotatedFatalf(t, "pod output check failed", "pod output check failed:\n%s\nCommand output:\n%s", err, out)
}
})
t.Run("get pods from the default namespace of current context", func(t *testing.T) {
out, err := TestHelper.Kubectl("", "config", "set-context", "--namespace="+TestHelper.GetLinkerdNamespace(), "--current")
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
out, err = TestHelper.LinkerdRun("get", "pods")
if err != nil {
testutil.AnnotatedFatal(t, "unexpected error", err)
}
err = checkPodOutput(out, linkerdPods, "linkerd-heartbeat", TestHelper.GetLinkerdNamespace())
if err != nil {
testutil.AnnotatedFatalf(t, "pod output check failed", "pod output check failed:\n%s\nCommand output:\n%s", err, out)
}
out, err = TestHelper.Kubectl("", "config", "set-context", "--namespace=default", "--current")
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
})
})
}
func checkPodOutput(cmdOutput string, expectedPodCounts map[string]int, optionalPod string, namespace string) error {
expectedPods := []string{}
for podName, replicas := range expectedPodCounts {
for i := 0; i < replicas; i++ {
expectedPods = append(expectedPods, podName)
}
}
lines := strings.Split(cmdOutput, "\n")
if len(lines) == 0 {
return fmt.Errorf("Expecting linkerd get pods to return something, got nothing")
}
var actualPods []string
for _, line := range lines {
sanitizedLine := strings.TrimSpace(line)
if sanitizedLine == "" {
continue
}
ns, pod, err := TestHelper.ParseNamespacedResource(sanitizedLine)
if err != nil {
return fmt.Errorf("Unexpected error: %v", err)
}
if ns == namespace {
podPrefix, err := parsePodPrefix(pod)
if err != nil {
return fmt.Errorf("Unexpected error: %v", err)
}
actualPods = append(actualPods, podPrefix)
}
}
sort.Strings(expectedPods)
sort.Strings(actualPods)
if !reflect.DeepEqual(expectedPods, actualPods) {
if optionalPod == "" {
return fmt.Errorf("Expected linkerd get to return:\n%v\nBut got:\n%v", expectedPods, actualPods)
}
expectedPlusOptionalPods := append(expectedPods, optionalPod)
sort.Strings(expectedPlusOptionalPods)
if !reflect.DeepEqual(expectedPlusOptionalPods, actualPods) {
return fmt.Errorf("Expected linkerd get to return:\n%v\nor:\n%v\nBut got:\n%v", expectedPods, expectedPlusOptionalPods, actualPods)
}
}
return nil
}
func parsePodPrefix(pod string) (string, error) {
r := regexp.MustCompile("^(.+)-.+-.+$")
matches := r.FindAllStringSubmatch(pod, 1)
if len(matches) == 0 {
return "", fmt.Errorf("string [%s] didn't contain expected format for pod name, extracted: %v", pod, matches)
}
return matches[0][1], nil
}

View File

@ -1,47 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cli-get-test-not-injected-d1
spec:
replicas: 2
selector:
matchLabels:
app: cli-get-test-not-injected-d1
template:
metadata:
labels:
app: cli-get-test-not-injected-d1
spec:
containers:
- name: http-to-grpc
image: buoyantio/bb:v0.0.6
args: ["terminus", "--grpc-server-port", "9090", "--response-text", "BANANA"]
ports:
- containerPort: 9090
- name: http-to-grpc-2
image: buoyantio/bb:v0.0.6
args: ["terminus", "--grpc-server-port", "90", "--response-text", "BANANA"]
ports:
- containerPort: 90
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cli-get-test-not-injected-d2
spec:
replicas: 1
selector:
matchLabels:
app: cli-get-test-not-injected-d2
template:
metadata:
labels:
app: cli-get-test-not-injected-d2
spec:
containers:
- name: http-to-grpc
image: buoyantio/bb:v0.0.6
args: ["terminus", "--grpc-server-port", "90", "--response-text", "BANANA"]
ports:
- containerPort: 90

View File

@ -1,47 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cli-get-test-d1
spec:
replicas: 2
selector:
matchLabels:
app: cli-get-test-d1
template:
metadata:
labels:
app: cli-get-test-d1
spec:
containers:
- name: http-to-grpc
image: buoyantio/bb:v0.0.6
args: ["terminus", "--grpc-server-port", "9090", "--response-text", "BANANA"]
ports:
- containerPort: 9090
- name: http-to-grpc-2
image: buoyantio/bb:v0.0.6
args: ["terminus", "--grpc-server-port", "90", "--response-text", "BANANA"]
ports:
- containerPort: 90
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cli-get-test-d2
spec:
replicas: 1
selector:
matchLabels:
app: cli-get-test-d2
template:
metadata:
labels:
app: cli-get-test-d2
spec:
containers:
- name: http-to-grpc
image: buoyantio/bb:v0.0.6
args: ["terminus", "--grpc-server-port", "90", "--response-text", "BANANA"]
ports:
- containerPort: 90