diff --git a/.ci/test b/.ci/test index a59bf2c17..6232ff063 100755 --- a/.ci/test +++ b/.ci/test @@ -14,35 +14,12 @@ fi cd "${SOURCE_PATH}" - -function check() { -if [ $? -eq 0 ]; then - echo "PASS" -else - echo "FAIL" -fi -} - -if [ $1x == "local"x ]; then -echo "====aws cli====" -go run cmd/gardenctl/main.go aws -- --version > /dev/null -check -echo "====az cli====" -go run cmd/gardenctl/main.go az -- -h > /dev/null -check -echo "====gcloud cli====" -go run cmd/gardenctl/main.go gcloud -- -v > /dev/null -check - -else # Build the Ginkgo (test framework) binary to be able to execute the tests. go install -mod=vendor ./vendor/github.com/onsi/ginkgo/ginkgo ############################################################################### ginkgo -v -progress -cover -r -mod=vendor pkg/... -fi - diff --git a/.ci/test_local b/.ci/test_local new file mode 100755 index 000000000..4833da2cd --- /dev/null +++ b/.ci/test_local @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +set -xeo pipefail + +# For the test step concourse will set the following environment variables: +# SOURCE_PATH - path to component repository root directory. + +if [[ -z "${SOURCE_PATH}" ]]; then + export SOURCE_PATH="$(readlink -f "$(dirname ${0})/..")" +else + export SOURCE_PATH="$(readlink -f "${SOURCE_PATH}")" +fi + +gardener_project="gardenctl" +shoot_namespace="garden-${gardener_project}" +gardener_url="https://gardener.garden.dev.k8s.ondemand.com" +gardenctl_cmd=( "go" "run" "cmd/gardenctl/main.go" ) +shoot_name="gctl-aws" + +function check() { + if [ $? -eq 0 ]; then + echo "PASS" + else + echo "FAIL" + fi +} + +gardenctl_clis(){ + echo "====aws cli====" + go run cmd/gardenctl/main.go target --server https://gardener.garden.dev.k8s.ondemand.com --project gardenctl --shoot gctl-aws > /dev/null + go run cmd/gardenctl/main.go aws --version > /dev/null && check + echo "====az cli====" + go run cmd/gardenctl/main.go target --server https://gardener.garden.dev.k8s.ondemand.com --project gardenctl --shoot gctl-az > /dev/null + go run cmd/gardenctl/main.go az -h > /dev/null && check + echo "====gcloud cli====" + go run cmd/gardenctl/main.go target --server https://gardener.garden.dev.k8s.ondemand.com --project gardenctl --shoot gctl-gcp > /dev/null + go run cmd/gardenctl/main.go gcloud -v > /dev/null && check +} + + +gardenctl_get(){ + echo "=====target/seed/shoot=====" + go run cmd/gardenctl/main.go target --server https://gardener.garden.dev.k8s.ondemand.com --seed aws --shoot gctl-aws > /dev/null && check + + echo "===get===garden/seed/shoot===" + go run cmd/gardenctl/main.go get garden > /dev/null && check + go run cmd/gardenctl/main.go get seed > /dev/null && check + go run cmd/gardenctl/main.go get shoot > /dev/null && check + + echo "=====target/project/shoot=====" + go run cmd/gardenctl/main.go target --server https://gardener.garden.dev.k8s.ondemand.com --project gardenctl --shoot gctl-aws > /dev/null && check + echo "===get===project/shoot/seed===" + go run cmd/gardenctl/main.go get garden > /dev/null && check + go run cmd/gardenctl/main.go get project > /dev/null && check + go run cmd/gardenctl/main.go get shoot > /dev/null && check + go run cmd/gardenctl/main.go get seed > /dev/null && check +} + +gardenctl_logs(){ + echo "===logs===logPodGardenImproved/logPod/logsKubernetesDashboard===" + go run cmd/gardenctl/main.go logs gardener-controller-manager > /dev/null && check + go run cmd/gardenctl/main.go logs api > /dev/null && check + go run cmd/gardenctl/main.go logs kubernetes-dashboard > /dev/null && check + +} + +gardenctl_show(){ + echo "===show===showPrometheus/showGrafana===" + go run cmd/gardenctl/main.go show prometheus > /dev/null && check + go run cmd/gardenctl/main.go show grafana > /dev/null && check +} + +gardenctl_ssh(){ + echo "===SSH===" + go run cmd/gardenctl/main.go ssh > /dev/null && check +} + +gardenctl_infra(){ + echo "===infra===" + go run cmd/gardenctl/main.go infra orphan list > /dev/null && check + +} + + +gardenctl_clis +gardenctl_get +gardenctl_logs +gardenctl_show +gardenctl_ssh +gardenctl_infra \ No newline at end of file diff --git a/pkg/cmd/download.go b/pkg/cmd/download.go index 776da3e57..2bf8769f0 100644 --- a/pkg/cmd/download.go +++ b/pkg/cmd/download.go @@ -60,7 +60,7 @@ func downloadTerraformFiles(option string, targetReader TargetReader) string { namespace := "" target := targetReader.ReadTarget(pathTarget) // return path allow non operator download key file - if getRole() == "user" { + if getRole(targetReader) == "user" { if (len(target.Stack()) < 3) || (len(target.Stack()) == 3 && target.Stack()[2].Kind == "namespace") { fmt.Println("No Shoot targeted") os.Exit(2) diff --git a/pkg/cmd/get.go b/pkg/cmd/get.go index 2718ff11c..0490e9020 100644 --- a/pkg/cmd/get.go +++ b/pkg/cmd/get.go @@ -15,16 +15,13 @@ package cmd import ( - "bytes" - "encoding/json" "errors" "fmt" + "io" "path/filepath" - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - yaml2 "github.com/ghodss/yaml" + "github.com/gardener/gardener/pkg/apis/core/v1beta1" "github.com/spf13/cobra" - yaml "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -39,57 +36,51 @@ func NewGetCmd(targetReader TargetReader, configReader ConfigReader, if len(args) < 1 || len(args) > 2 { return errors.New("command must be in the format: get [(garden|project|seed|shoot|target) ]") } + + name := "" + if len(args) == 2 { + name = args[1] + } + switch args[0] { case "project": - if len(args) == 1 { - err = getProject("", targetReader, ioStreams) - if err != nil { - return err - } - } else if len(args) == 2 { - err = getProject(args[1], targetReader, ioStreams) - if err != nil { - return err - } + if IsTargeted(targetReader, "project") { + err = printProjectKubeconfig(name, targetReader, ioStreams.Out, outputFormat) + checkError(err) + } else { + return errors.New("no project targeted") } + case "garden": - if len(args) == 1 { - err = getGarden("", configReader, targetReader, kubeconfigReader, ioStreams) - if err != nil { - return err - } - } else if len(args) == 2 { - err = getGarden(args[1], configReader, targetReader, kubeconfigReader, ioStreams) - if err != nil { - return err - } + if IsTargeted(targetReader, "garden") { + err = printGardenKubeconfig(name, configReader, targetReader, kubeconfigReader, ioStreams.Out, outputFormat) + checkError(err) + } else { + return errors.New("no garden targeted") } + case "seed": - if len(args) == 1 { - err = getSeed("", targetReader, ioStreams) - if err != nil { - return err - } - } else if len(args) == 2 { - err = getSeed(args[1], targetReader, ioStreams) - if err != nil { - return err - } + if IsTargeted(targetReader, "seed") || IsTargeted(targetReader, "project", "shoot") { + err = printSeedKubeconfig(name, targetReader, ioStreams.Out, outputFormat) + checkError(err) + } else { + return errors.New("no seed targeted targeted or shoot targeted") } + case "shoot": - if len(args) == 1 { - err = getShoot("", targetReader, kubeconfigWriter, ioStreams) - if err != nil { - return err - } - } else if len(args) == 2 { - err = getShoot(args[1], targetReader, kubeconfigWriter, ioStreams) - if err != nil { - return err - } + if IsTargeted(targetReader, "shoot") { + err = printShootKubeconfig(name, targetReader, kubeconfigWriter, ioStreams.Out, outputFormat) + checkError(err) + } else { + return errors.New("no shoot targeted") } + case "target": - err = getTarget(targetReader, ioStreams) + if !IsTargeted(targetReader) { + return errors.New("target stack is empty") + } + + err = printTarget(targetReader, ioStreams.Out, outputFormat) if err != nil { return err } @@ -105,52 +96,29 @@ func NewGetCmd(targetReader TargetReader, configReader ConfigReader, return cmd } -// getProject lists -func getProject(name string, targetReader TargetReader, ioStreams IOStreams) error { - target := targetReader.ReadTarget(pathTarget) +// printProjectKubeconfig lists +func printProjectKubeconfig(name string, targetReader TargetReader, writer io.Writer, outFormat string) error { + var err error + var project *v1beta1.Project if name == "" { - if len(target.Stack()) < 2 { - return errors.New("no project targeted") - } else if len(target.Stack()) > 1 && target.Stack()[1].Kind == "project" { - name = target.Stack()[1].Name - } else if len(target.Stack()) > 1 && target.Stack()[1].Kind == "seed" { - return errors.New("seed targeted, project expected") - } + project, err = GetTargetedProjectObject(targetReader) + } else { + project, err = GetProjectObject(targetReader, name) } - clientset, err := target.GardenerClient() - if err != nil { - return err - } - project, err := clientset.CoreV1beta1().Projects().Get(name, metav1.GetOptions{}) + if err != nil { return err } - if outputFormat == "yaml" { - var output []byte - if output, err = yaml.Marshal(project); err != nil { - return err - } - fmt.Fprint(ioStreams.Out, string(output)) - } else if outputFormat == "json" { - var output []byte - if output, err = json.MarshalIndent(project, "", " "); err != nil { - return err - } - fmt.Fprint(ioStreams.Out, string(output)) - } - return nil + return PrintoutObject(project, writer, outFormat) } -// getGarden lists kubeconfig of garden cluster -func getGarden(name string, configReader ConfigReader, targetReader TargetReader, kubeconfigReader KubeconfigReader, ioStreams IOStreams) error { +// printGardenKubeconfig lists kubeconfig of garden cluster +func printGardenKubeconfig(name string, configReader ConfigReader, targetReader TargetReader, kubeconfigReader KubeconfigReader, writer io.Writer, outFormat string) error { if name == "" { - target := targetReader.ReadTarget(pathTarget) - if len(target.Stack()) > 0 { - name = target.Stack()[0].Name - } else { - return errors.New("no garden targeted") - } + var err error + name, err = GetTargetName(targetReader, "garden") + checkError(err) } config := configReader.ReadConfig(pathGardenConfig) @@ -163,20 +131,8 @@ func getGarden(name string, configReader ConfigReader, targetReader TargetReader if err != nil { return err } - if outputFormat == "yaml" { - fmt.Fprintf(ioStreams.Out, "%s\n", kubeconfig) - } else if outputFormat == "json" { - y, err := yaml2.YAMLToJSON(kubeconfig) - if err != nil { - return err - } - var out bytes.Buffer - if err = json.Indent(&out, y, "", " "); err != nil { - return err - } - fmt.Fprint(ioStreams.Out, out.String()) - } - match = true + + return PrintoutObject(kubeconfig, writer, outFormat) } } if !match { @@ -186,113 +142,56 @@ func getGarden(name string, configReader ConfigReader, targetReader TargetReader return nil } -// getSeed lists kubeconfig of seed cluster -func getSeed(name string, targetReader TargetReader, ioStreams IOStreams) error { +// printSeedKubeconfig lists kubeconfig of seed cluster +func printSeedKubeconfig(name string, targetReader TargetReader, writer io.Writer, outFormat string) error { target := targetReader.ReadTarget(pathTarget) - if name == "" { - if len(target.Stack()) > 1 && target.Stack()[1].Kind == "seed" { - name = target.Stack()[1].Name - } else if len(target.Stack()) > 1 && target.Stack()[1].Kind == "project" && len(target.Stack()) == 3 { - name = getSeedForProject(target.Stack()[2].Name) - } else { - return errors.New("no seed targeted or shoot targeted") - } - } + client, err := target.K8SClientToKind(TargetKindGarden) if err != nil { return err } - gardenClientset, err := target.GardenerClient() - if err != nil { - return err - } - seed, err := gardenClientset.CoreV1beta1().Seeds().Get(name, metav1.GetOptions{}) - if err != nil { - return err - } - kubeSecret, err := client.CoreV1().Secrets(seed.Spec.SecretRef.Namespace).Get(seed.Spec.SecretRef.Name, metav1.GetOptions{}) - if err != nil { - return err - } - if outputFormat == "yaml" { - fmt.Fprintf(ioStreams.Out, "%s\n", kubeSecret.Data["kubeconfig"]) - } else if outputFormat == "json" { - y, err := yaml2.YAMLToJSON(kubeSecret.Data["kubeconfig"]) + var seed *v1beta1.Seed + if name == "" { + seed, err = GetTargetedSeedObject(targetReader) if err != nil { return err } - var out bytes.Buffer - if err = json.Indent(&out, y, "", " "); err != nil { + } else { + seed, err = GetSeedObject(targetReader, name) + if err != nil { return err } - fmt.Fprint(ioStreams.Out, out.String()) } - return nil + kubeSecret, err := client.CoreV1().Secrets(seed.Spec.SecretRef.Namespace).Get(seed.Spec.SecretRef.Name, metav1.GetOptions{}) + if err != nil { + return err + } + + return PrintoutObject(fmt.Sprintf("%s\n", kubeSecret.Data["kubeconfig"]), writer, outFormat) } -// getShoot lists kubeconfig of shoot -func getShoot(name string, targetReader TargetReader, kubeconfigWriter KubeconfigWriter, ioStreams IOStreams) error { +// printShootKubeconfig lists kubeconfig of shoot +func printShootKubeconfig(name string, targetReader TargetReader, kubeconfigWriter KubeconfigWriter, writer io.Writer, outFormat string) error { target := targetReader.ReadTarget(pathTarget) - if name == "" { - if !CheckShootIsTargeted(target) { - return errors.New("no shoot targeted") - } - } else if name != "" { - if len(target.Stack()) < 2 { - return errors.New("no seed or project targeted") - } - } + client, err := target.K8SClientToKind(TargetKindGarden) if err != nil { return err } - gardenClientset, err := target.GardenerClient() - if err != nil { - return err - } - var namespace string - var shoot *gardencorev1beta1.Shoot - if target.Stack()[1].Kind == "project" { - project, err := gardenClientset.CoreV1beta1().Projects().Get(target.Stack()[1].Name, metav1.GetOptions{}) - if err != nil { - return err - } - if name == "" { - shoot, err = gardenClientset.CoreV1beta1().Shoots(*project.Spec.Namespace).Get(target.Stack()[2].Name, metav1.GetOptions{}) - if err != nil { - return err - } - } - if name != "" { - shoot, err = gardenClientset.CoreV1beta1().Shoots(*project.Spec.Namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return err - } - } - namespace = shoot.Status.TechnicalID - } - if target.Stack()[1].Kind == "seed" { - shootList, err := gardenClientset.CoreV1beta1().Shoots("").List(metav1.ListOptions{}) - if err != nil { - return err - } - for index, s := range shootList.Items { - if s.Name == target.Stack()[2].Name && *s.Spec.SeedName == target.Stack()[1].Name { - if (name == "") && (s.Name == target.Stack()[2].Name) { - shoot = &shootList.Items[index] - namespace = shootList.Items[index].Status.TechnicalID - break - } - if (name != "") && (s.Name == name) { - shoot = &shootList.Items[index] - namespace = shootList.Items[index].Status.TechnicalID - break - } - } - } + + var shoot *v1beta1.Shoot + if name == "" { + shoot, err = GetTargetedShootObject(targetReader) + checkError(err) + } else { + shoot, err = GetShootObject(targetReader, name) + checkError(err) } - seed, err := gardenClientset.CoreV1beta1().Seeds().Get(*shoot.Spec.SeedName, metav1.GetOptions{}) + + namespace := shoot.Status.TechnicalID + + seed, err := GetTargetedSeedObject(targetReader) if err != nil { return err } @@ -300,7 +199,8 @@ func getShoot(name string, targetReader TargetReader, kubeconfigWriter Kubeconfi if err != nil { return err } - gardenName := target.Stack()[0].Name + gardenName, err := GetTargetName(targetReader, "garden") + checkError(err) kubeconfigPath := filepath.Join(pathGardenHome, "cache", gardenName, "seeds", seed.Spec.SecretRef.Name, "kubeconfig.yaml") err = kubeconfigWriter.Write(kubeconfigPath, kubeSecret.Data["kubeconfig"]) checkError(err) @@ -310,47 +210,17 @@ func getShoot(name string, targetReader TargetReader, kubeconfigWriter Kubeconfi if err != nil { return err } + kubeSecret, err = seedClient.CoreV1().Secrets(namespace).Get("kubecfg", metav1.GetOptions{}) if err != nil { return err } - if outputFormat == "yaml" { - fmt.Fprintf(ioStreams.Out, "%s\n", kubeSecret.Data["kubeconfig"]) - } else if outputFormat == "json" { - y, err := yaml2.YAMLToJSON(kubeSecret.Data["kubeconfig"]) - if err != nil { - return err - } - var out bytes.Buffer - if err = json.Indent(&out, y, "", " "); err != nil { - return err - } - fmt.Fprint(ioStreams.Out, out.String()) - } - return nil + return PrintoutObject(fmt.Sprintf("%s\n", kubeSecret.Data["kubeconfig"]), writer, outFormat) } -// getTarget prints the target stack. -func getTarget(targetReader TargetReader, ioStreams IOStreams) (err error) { +// printTarget prints the target stack. +func printTarget(targetReader TargetReader, writer io.Writer, outFormat string) (err error) { target := targetReader.ReadTarget(pathTarget) - if len(target.Stack()) == 0 { - return errors.New("target stack is empty") - } - - if outputFormat == "yaml" { - var output []byte - if output, err = yaml.Marshal(target); err != nil { - return err - } - fmt.Fprint(ioStreams.Out, string(output)) - } else if outputFormat == "json" { - var output []byte - if output, err = json.MarshalIndent(target, "", " "); err != nil { - return err - } - fmt.Fprint(ioStreams.Out, string(output)) - } - - return nil + return PrintoutObject(target, writer, outFormat) } diff --git a/pkg/cmd/get_test.go b/pkg/cmd/get_test.go index f8a204fc7..82f51402c 100644 --- a/pkg/cmd/get_test.go +++ b/pkg/cmd/get_test.go @@ -69,7 +69,7 @@ var _ = Describe("Get command", func() { Context("missing target", func() { It("shout return error for missing shoot in the target", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() target.EXPECT().Stack().Return([]cmd.TargetMeta{}) ioStreams, _, _, _ := cmd.NewTestIOStreams() @@ -82,7 +82,7 @@ var _ = Describe("Get command", func() { }) It("shout return error for missing target object", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() target.EXPECT().Stack().Return([]cmd.TargetMeta{}) ioStreams, _, _, _ := cmd.NewTestIOStreams() @@ -97,7 +97,7 @@ var _ = Describe("Get command", func() { Context("target shoot with valid target object", func() { seedName := "test-seed" - + nameSpace := "test-namespace" k8sClientToGarden := kubernetesfake.NewSimpleClientset( &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -133,12 +133,28 @@ var _ = Describe("Get command", func() { }, &gardencorev1beta1.Shoot{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-shoot", + Name: "test-shoot", + Namespace: "test-namespace", }, Spec: gardencorev1beta1.ShootSpec{ SeedName: &seedName, }, - }) + }, + &gardencorev1beta1.ShootList{ + Items: []gardencorev1beta1.Shoot{}, + }, + &gardencorev1beta1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-project", + }, + Spec: gardencorev1beta1.ProjectSpec{ + Namespace: &nameSpace, + }, + }, + &gardencorev1beta1.ProjectList{ + Items: []gardencorev1beta1.Project{}, + }, + ) targetMeta := []cmd.TargetMeta{ { @@ -150,7 +166,7 @@ var _ = Describe("Get command", func() { Name: "test-seed", }, { - Kind: cmd.TargetKindSeed, + Kind: cmd.TargetKindShoot, Name: "test-shoot", }, } @@ -166,7 +182,7 @@ var _ = Describe("Get command", func() { kubeconfig := []byte("test-kubeconfig") It("should pass on get garden", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() configReader.EXPECT().ReadConfig(gomock.Any()).Return(gardenConfig) kubeconfigReader.EXPECT().ReadKubeconfig(gomock.Any()).Return(kubeconfig, nil) target.EXPECT().Stack().Return(targetMeta).AnyTimes() @@ -180,10 +196,10 @@ var _ = Describe("Get command", func() { }) It("should pass on get seed", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() target.EXPECT().K8SClientToKind(cmd.TargetKindGarden).Return(k8sClientToGarden, nil) target.EXPECT().Stack().Return(targetMeta).AnyTimes() - target.EXPECT().GardenerClient().Return(clientSet, nil) + target.EXPECT().GardenerClient().Return(clientSet, nil).AnyTimes() ioStreams, _, _, _ := cmd.NewTestIOStreams() command = cmd.NewGetCmd(targetReader, configReader, kubeconfigReader, kubeconfigWriter, ioStreams) @@ -194,11 +210,11 @@ var _ = Describe("Get command", func() { }) It("should pass on get shoot", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) - target.EXPECT().K8SClientToKind(cmd.TargetKindGarden).Return(k8sClientToGarden, nil) - target.EXPECT().K8SClientToKind(cmd.TargetKindSeed).Return(k8sClientToGarden, nil) + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() + target.EXPECT().K8SClientToKind(cmd.TargetKindGarden).Return(k8sClientToGarden, nil).AnyTimes() + target.EXPECT().K8SClientToKind(cmd.TargetKindSeed).Return(k8sClientToGarden, nil).AnyTimes() target.EXPECT().Stack().Return(targetMeta).AnyTimes() - target.EXPECT().GardenerClient().Return(clientSet, nil) + target.EXPECT().GardenerClient().Return(clientSet, nil).AnyTimes() kubeconfigWriter.EXPECT().Write(gomock.Any(), gomock.Any()).Return(nil) ioStreams, _, _, _ := cmd.NewTestIOStreams() @@ -210,20 +226,21 @@ var _ = Describe("Get command", func() { }) It("should pass on get target", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) - target.EXPECT().Stack().Return(targetMeta).AnyTimes() - target.EXPECT().Stack().Return(targetMeta).AnyTimes() + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() + target.EXPECT().Stack().Return([]cmd.TargetMeta{}) ioStreams, _, _, _ := cmd.NewTestIOStreams() command = cmd.NewGetCmd(targetReader, configReader, kubeconfigReader, kubeconfigWriter, ioStreams) command.SetArgs([]string{"target"}) err := command.Execute() - Expect(err).NotTo(HaveOccurred()) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("target stack is empty")) + }) It("should fail on get project", func() { - targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() target.EXPECT().Stack().Return(targetMeta).AnyTimes() ioStreams, _, _, _ := cmd.NewTestIOStreams() @@ -232,7 +249,7 @@ var _ = Describe("Get command", func() { err := command.Execute() Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("seed targeted, project expected")) + Expect(err.Error()).To(Equal("no project targeted")) }) }) }) diff --git a/pkg/cmd/infra.go b/pkg/cmd/infra.go index c27fd9eb0..2fc9f65cd 100644 --- a/pkg/cmd/infra.go +++ b/pkg/cmd/infra.go @@ -60,15 +60,15 @@ func NewInfraCmd(targetReader TargetReader) *cobra.Command { switch infraType { case "aws": - rs = getAWSInfraResources() + rs = getAWSInfraResources(targetReader) case "azure": - rs = getAzureInfraResources() + rs = getAzureInfraResources(targetReader) case "gcp": - rs = getGCPInfraResources() + rs = getGCPInfraResources(targetReader) case "openstack": - rs = getOstackInfraResources() + rs = getOstackInfraResources(targetReader) case "alicloud": - rs = getAliCloudInfraResources() + rs = getAliCloudInfraResources(targetReader) default: return errors.New("infra type not found") } @@ -112,68 +112,68 @@ func GetOrphanInfraResources(rs []string, terraformstate string) error { return nil } -func getAWSInfraResources() []string { +func getAWSInfraResources(targetReader TargetReader) []string { rs := make([]string, 0) - shoottag := getFromTargetInfo("shootTechnicalID") + shoottag := GetFromTargetInfo(targetReader, "shootTechnicalID") // fetch shoot vpc resources - capturedOutput := execInfraOperator("aws", "aws ec2 describe-vpcs --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput := execInfraOperator("aws", "ec2 describe-vpcs --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`VPCS.*(vpc-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot subnet resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-subnets --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-subnets --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`SUBNETS.*:subnet\/(subnet-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot dhcp options resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-dhcp-options --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-dhcp-options --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`DHCPOPTIONS.*(dopt-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot ip address resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-addresses --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-addresses --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`ADDRESSES.*(eipalloc-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot nat gateway resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-nat-gateways --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-nat-gateways --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`NATGATEWAYS.*(nat-[a-z0-9]*)`, capturedOutput, rs) rs = findInfraResourcesMatch(`NATGATEWAYADDRESSES.*(eni-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot internet gateway resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-internet-gateways --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-internet-gateways --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`INTERNETGATEWAYS.*(igw-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot security group resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-security-groups --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-security-groups --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`SECURITYGROUPS.*(sg-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot route table resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-route-tables --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-route-tables --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`ROUTETABLES.*(rtb-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot instance resources - capturedOutput = execInfraOperator("aws", "aws ec2 describe-instances --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") + capturedOutput = execInfraOperator("aws", "ec2 describe-instances --filter Name=tag:kubernetes.io/cluster/"+shoottag+",Values=1") rs = findInfraResourcesMatch(`IAMINSTANCEPROFILE.*:instance-profile\/(shoot--[a-z0-9-]*-nodes)`, capturedOutput, rs) // fetch shoot bastion instance resource - capturedOutput = execInfraOperator("aws", "aws ec2 describe-instances --filter Name=tag:Name,Values="+shoottag+"-bastions") + capturedOutput = execInfraOperator("aws", "ec2 describe-instances --filter Name=tag:Name,Values="+shoottag+"-bastions") rs = findInfraResourcesMatch(`INSTANCES.*(i-[a-z0-9]*)`, capturedOutput, rs) // fetch shoot bastion security group - capturedOutput = execInfraOperator("aws", "aws ec2 describe-security-groups --filter Name=tag:component,Values=gardenctl") + capturedOutput = execInfraOperator("aws", "ec2 describe-security-groups --filter Name=tag:component,Values=gardenctl") rs = findInfraResourcesMatch("SECURITYGROUPS.*(sg-[a-z0-9]*).*"+shoottag, capturedOutput, rs) return unique(rs) } -func getAzureInfraResources() []string { +func getAzureInfraResources(targetReader TargetReader) []string { rs := make([]string, 0) - shoottag := getFromTargetInfo("shootTechnicalID") + shoottag := GetFromTargetInfo(targetReader, "shootTechnicalID") // fetch shoot resource group - capturedOutput := execInfraOperator("az", "az group show --name "+shoottag) + capturedOutput := execInfraOperator("az", "group show --name "+shoottag) rs = findInfraResourcesMatch(`\"id\".*(resourceGroups\/[a-z0-9-]*)\"`, capturedOutput, rs) // fetch shoot vnet resources - capturedOutput = execInfraOperator("az", "az network vnet list -g "+shoottag) + capturedOutput = execInfraOperator("az", "network vnet list -g "+shoottag) vnets := make([]string, 0) vnets = findInfraResourcesMatch(`\"id\".*(virtualNetworks\/[a-z0-9-]*)\"`, capturedOutput, vnets) rs = findInfraResourcesMatch(`\"id\".*(virtualNetworks\/[a-z0-9-]*)\"`, capturedOutput, rs) @@ -183,32 +183,32 @@ func getAzureInfraResources() []string { for _, vnet := range vnets { s := strings.Split(vnet, "/") vnetName := s[1] - capturedOutput = execInfraOperator("az", "az network vnet subnet list -g "+shoottag+" --vnet-name "+vnetName) + capturedOutput = execInfraOperator("az", "network vnet subnet list -g "+shoottag+" --vnet-name "+vnetName) rs = findInfraResourcesMatch(`\"id\".*(subnets\/[a-z0-9-]*)\"`, capturedOutput, rs) } } // fetch shoot nic resources - capturedOutput = execInfraOperator("az", "az network nic list -g "+shoottag) + capturedOutput = execInfraOperator("az", "network nic list -g "+shoottag) rs = findInfraResourcesMatch(`\"id\".*(networkInterfaces\/[a-z0-9-]*)\"`, capturedOutput, rs) // fetch shoot security group resources - capturedOutput = execInfraOperator("az", "az network nsg list -g "+shoottag) + capturedOutput = execInfraOperator("az", "network nsg list -g "+shoottag) rs = findInfraResourcesMatch(`\"id\".*(networkSecurityGroups\/[a-z0-9-]*)\"`, capturedOutput, rs) // fetch shoot route resources - capturedOutput = execInfraOperator("az", "az network route-table list -g "+shoottag) + capturedOutput = execInfraOperator("az", "network route-table list -g "+shoottag) rs = findInfraResourcesMatch(`\"id\".*routes\/([a-z0-9-]*)\"`, capturedOutput, rs) return unique(rs) } -func getGCPInfraResources() []string { +func getGCPInfraResources(targetReader TargetReader) []string { rs := make([]string, 0) - shoottag := getFromTargetInfo("shootTechnicalID") + shoottag := GetFromTargetInfo(targetReader, "shootTechnicalID") // fetch shoot subnet resource - capturedOutput := execInfraOperator("gcp", "gcloud compute networks subnets list") + capturedOutput := execInfraOperator("gcp", "compute networks subnets list") if strings.Contains(capturedOutput, shoottag+"-nodes") { rsShootSubnet := make([]string, 0) rsShootSubnet = findInfraResourcesMatch(shoottag+"-nodes(.*)", capturedOutput, rsShootSubnet) @@ -218,13 +218,13 @@ func getGCPInfraResources() []string { rs = append(rs, shoottag+"-nodes") // fetch shoot vpc resource - capturedOutput = execInfraOperator("gcp", "gcloud compute networks list") + capturedOutput = execInfraOperator("gcp", "compute networks list") if strings.Contains(capturedOutput, shootVpc) { rs = append(rs, shootVpc) } // fetch shoot cloud router resource - capturedOutput = execInfraOperator("gcp", "gcloud compute routers list") + capturedOutput = execInfraOperator("gcp", "compute routers list") if strings.Contains(capturedOutput, shootVpc) { rsShootRouter := make([]string, 0) rsShootRouter = findInfraResourcesMatch("(.*)"+shootVpc, capturedOutput, rsShootRouter) @@ -236,7 +236,7 @@ func getGCPInfraResources() []string { rs = append(rs, shootRouter) // fetch shoot cloud nat resource - capturedOutput = execInfraOperator("gcp", "gcloud compute routers nats list --router="+shootRouter+" --router-region="+shootRouterRegion) + capturedOutput = execInfraOperator("gcp", "compute routers nats list --router="+shootRouter+" --router-region="+shootRouterRegion) if strings.Contains(capturedOutput, shoottag+"-cloud-nat") { rs = append(rs, shoottag+"-cloud-nat") } @@ -247,7 +247,7 @@ func getGCPInfraResources() []string { } // fetch shoot service account - capturedOutput = execInfraOperator("gcp", "gcloud iam service-accounts list") + capturedOutput = execInfraOperator("gcp", "iam service-accounts list") if strings.Contains(capturedOutput, shoottag) { rsserviceAccount := make([]string, 0) rsserviceAccount = findInfraResourcesMatch(shoottag+"(.*)False", capturedOutput, rsserviceAccount) @@ -262,9 +262,9 @@ func getGCPInfraResources() []string { return unique(rs) } -func getOstackInfraResources() []string { +func getOstackInfraResources(targetReader TargetReader) []string { rs := make([]string, 0) - shoottag := getFromTargetInfo("shootTechnicalID") + shoottag := GetFromTargetInfo(targetReader, "shootTechnicalID") // fetch shoot network id capturedOutput := execInfraOperator("openstack", "openstack network list") @@ -326,9 +326,9 @@ func getOstackInfraResources() []string { return unique(rs) } -func getAliCloudInfraResources() []string { +func getAliCloudInfraResources(targetReader TargetReader) []string { rs := make([]string, 0) - shoottag := getFromTargetInfo("shootTechnicalID") + shoottag := GetFromTargetInfo(targetReader, "shootTechnicalID") // fetch shoot vpc id capturedOutput := execInfraOperator("aliyun", "aliyun vpc DescribeVpcs --VpcName "+shoottag+"-vpc") @@ -429,10 +429,7 @@ func getAliCloudInfraResources() []string { } func execInfraOperator(provider string, arguments string) string { - captured := capture() - operate(provider, arguments) - capturedOutput, err := captured() - checkError(err) + capturedOutput := operate(provider, arguments) return capturedOutput } diff --git a/pkg/cmd/logs.go b/pkg/cmd/logs.go index 1793d3c0a..1b75f75ae 100644 --- a/pkg/cmd/logs.go +++ b/pkg/cmd/logs.go @@ -18,6 +18,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "os" "path" "path/filepath" @@ -42,19 +43,19 @@ const ( var flags *logFlags // NewLogsCmd returns a new logs command. -func NewLogsCmd() *cobra.Command { +func NewLogsCmd(targetReader TargetReader) *cobra.Command { flags = newLogsFlags() cmd := &cobra.Command{ Use: "logs (gardener-apiserver|gardener-controller-manager|gardener-dashboard|api|scheduler|controller-manager|etcd-operator|etcd-main[etcd backup-restore]|etcd-main-backup|etcd-events[etcd backup-restore]|addon-manager|vpn-seed|vpn-shoot|machine-controller-manager|kubernetes-dashboard|prometheus|grafana|gardenlet|tf (infra|dns|ingress)|cluster-autoscaler)", Short: "Show and optionally follow logs of given component\n", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - err := validateArgs(args) + err := validateArgs(targetReader, args) if err != nil { return err } validateFlags(flags) - runCommand(args) + runCommand(targetReader, args) return nil }, ValidArgs: []string{"gardener-apiserver", "gardener-controller-manager", "gardener-dashboard", "api", "scheduler", "controller-manager", "etcd-operator", "etcd-main", "etcd-events", "addon-manager", "vpn-seed", "vpn-shoot", "auto-node-repair", "kubernetes-dashboard", "prometheus", "grafana", "gardenlet", "tf"}, @@ -68,17 +69,17 @@ func NewLogsCmd() *cobra.Command { return cmd } -func validateArgs(args []string) error { +func validateArgs(targetReader TargetReader, args []string) error { if len(args) < 1 || len(args) > 3 { return errors.New("Command must be in the format: logs (gardener-apiserver|gardener-controller-manager|gardener-dashboard|api|scheduler|controller-manager|etcd-operator|etcd-main[etcd backup-restore]|etcd-events[etcd backup-restore]|addon-manager|vpn-seed|vpn-shoot|machine-controller-manager|kubernetes-dashboard|prometheus|grafana|gardenlet|tf (infra|dns|ingress)|cluster-autoscaler flags(--loki|--tail|--since|--since-time|--timestamps)") } var t Target ReadTarget(pathTarget, &t) - if len(t.Target) < 3 && (args[0] != "gardener-apiserver") && (args[0] != "gardener-controller-manager") && (args[0] != "tf") && (args[0] != "kubernetes-dashboard") { + if !IsTargeted(targetReader, "shoot") && args[0] != "gardener-apiserver" && args[0] != "gardener-controller-manager" && args[0] != "tf" && args[0] != "kubernetes-dashboard" { return errors.New("No shoot targeted") - } else if (len(t.Target) < 2 && (args[0] == "tf")) || len(t.Target) < 3 && (args[0] == "tf") && (t.Target[1].Kind != "seed") { + } else if !IsTargeted(targetReader, "project") && args[0] == "tf" || !IsTargeted(targetReader, "shoot") && args[0] == "tf" && !IsTargeted(targetReader, "seed") { return errors.New("No seed or shoot targeted") - } else if len(t.Target) == 0 { + } else if !IsTargeted(targetReader) { return errors.New("Target stack is empty") } return nil @@ -105,56 +106,56 @@ func validateFlags(flags *logFlags) { } } -func runCommand(args []string) { +func runCommand(targetReader TargetReader, args []string) { switch args[0] { case "all": - saveLogsAll() + saveLogsAll(targetReader) case "gardener-apiserver": logsGardenerApiserver() case "gardener-controller-manager": - logsGardenerControllerManager() + logsGardenerControllerManager(targetReader) case "gardener-dashboard": logsGardenerDashboard() case "api": - logsAPIServer() + logsAPIServer(targetReader) case "scheduler": - logsScheduler() + logsScheduler(targetReader) case "controller-manager": - logsControllerManager() + logsControllerManager(targetReader) case "etcd-operator": logsEtcdOpertor() case "etcd-main": if len(args) == 2 { - logsEtcdMain(args[1]) + logsEtcdMain(targetReader, args[1]) } else { - logsEtcdMain(emptyString) + logsEtcdMain(targetReader, emptyString) } case "etcd-main-backup": - logsEtcdMainBackup() + logsEtcdMainBackup(targetReader) case "etcd-events": if len(args) == 2 { - logsEtcdEvents(args[1]) + logsEtcdEvents(targetReader, args[1]) } else { - logsEtcdEvents(emptyString) + logsEtcdEvents(targetReader, emptyString) } case "addon-manager": - logsAddonManager() + logsAddonManager(targetReader) case "vpn-seed": logsVpnSeed(args[1]) case "vpn-shoot": logsVpnShoot() case "machine-controller-manager": - logsMachineControllerManager() + logsMachineControllerManager(targetReader) case "kubernetes-dashboard": - logsKubernetesDashboard() + logsKubernetesDashboard(targetReader) case "prometheus": - logsPrometheus() + logsPrometheus(targetReader) case "grafana": - logsGrafana() + logsGrafana(targetReader) case "gardenlet": logsGardenlet() case "cluster-autoscaler": - logsClusterAutoscaler() + logsClusterAutoscaler(targetReader) case "tf": if len(args) == 1 || len(args) < 3 { logsTfHelp() @@ -180,7 +181,7 @@ func runCommand(args []string) { } } -func saveLogsAll() { +func saveLogsAll(targetReader TargetReader) { //flags.sinceSeconds = 600 * time.Second if _, err := os.Stat("./logs/"); !os.IsNotExist(err) { os.RemoveAll("./logs/") @@ -190,27 +191,27 @@ func saveLogsAll() { fmt.Println("APIServer/Scheduler/ControllerManager/etcd/AddonManager/VpnShoot/Dashboard/Prometheus/Gardenlet/Autoscaler logs will be downloaded") - saveLogsAPIServer() - saveLogsScheduler() - saveLogsControllerManager() - saveLogsEtcdMain("etcd") - saveLogsEtcdMain("backup-restore") - saveLogsEtcdMainBackup() - saveLogsEtcdEvents("etcd") - saveLogsEtcdEvents("backup-restore") - saveLogsAddonManager() + saveLogsAPIServer(targetReader) + saveLogsScheduler(targetReader) + saveLogsControllerManager(targetReader) + saveLogsEtcdMain(targetReader, "etcd") + saveLogsEtcdMain(targetReader, "backup-restore") + saveLogsEtcdMainBackup(targetReader) + saveLogsEtcdEvents(targetReader, "etcd") + saveLogsEtcdEvents(targetReader, "backup-restore") + saveLogsAddonManager(targetReader) saveLogsVpnShoot() - saveLogsMachineControllerManager() + saveLogsMachineControllerManager(targetReader) saveLogsKubernetesDashboard() - saveLogsPrometheus() + saveLogsPrometheus(targetReader) saveLogsGardenlet() - saveLogsClusterAutoscaler() - saveLogsGrafana() + saveLogsClusterAutoscaler(targetReader) + saveLogsGrafana(targetReader) var target Target ReadTarget(pathTarget, &target) if !(len(target.Target) < 3 || (len(target.Stack()) == 3 && target.Stack()[2].Kind == "namespace")) { - shoot, err := getShootObject() + shoot, err := GetTargetedShootObject(targetReader) checkError(err) saveLogsTerraform(shoot.Name + ".infra.tf") saveLogsTerraform(shoot.Name + ".dns.tf") @@ -225,16 +226,16 @@ func saveLogsAll() { } // showPod is an abstraction to show pods in seed cluster controlplane or kube-system namespace of shoot -func logPod(toMatch string, toTarget string, container string) { +func logPod(targetReader TargetReader, toMatch string, toTarget string, container string) { var target Target ReadTarget(pathTarget, &target) - if len(target.Target) < 3 || (len(target.Stack()) == 3 && target.Stack()[2].Kind == "namespace") { - fmt.Println("No shoot targeted") - os.Exit(2) + if !IsTargeted(targetReader, "shoot") { + log.Fatal("No shoot targeted") } - namespace := getSeedNamespaceNameForShoot(target.Target[2].Name) + + namespace := GetFromTargetInfo(targetReader, "shootTechnicalID") var err error - shoot, err := getShootObject() + shoot, err := GetTargetedShootObject(targetReader) checkError(err) gardenerVersion, err := semver.NewVersion(shoot.Status.Gardener.Version) @@ -265,7 +266,7 @@ func logPod(toMatch string, toTarget string, container string) { } // showPod is an abstraction to show pods in seed cluster controlplane or kube-system namespace of shoot -func saveLogPod(toMatch string, toTarget string, container string) { +func saveLogPod(targetReader TargetReader, toMatch string, toTarget string, container string) { var target Target ReadTarget(pathTarget, &target) if len(target.Target) < 3 || (len(target.Stack()) == 3 && target.Stack()[2].Kind == "namespace") { @@ -274,7 +275,7 @@ func saveLogPod(toMatch string, toTarget string, container string) { } namespace := getSeedNamespaceNameForShoot(target.Target[2].Name) var err error - shoot, err := getShootObject() + shoot, err := GetTargetedShootObject(targetReader) checkError(err) gardenerVersion, err := semver.NewVersion(shoot.Status.Gardener.Version) @@ -493,16 +494,16 @@ func saveLogPodShoot(toMatch, namespace string, container string) { } // logPodGardenImproved print logfiles for garden pods -func logPodGardenImproved(podName string) { +func logPodGardenImproved(targetReader TargetReader, podName string) { var target Target ReadTarget(pathTarget, &target) Client, err := clientToTarget("garden") checkError(err) pods, err := Client.CoreV1().Pods("garden").List(metav1.ListOptions{}) checkError(err) - project, err := getTargetName("project") + project, err := GetTargetName(targetReader, "project") checkError(err) - shootName, err := getTargetName("shoot") + shootName, err := GetTargetName(targetReader, "shoot") checkError(err) for _, pod := range pods.Items { @@ -530,13 +531,13 @@ func logsGardenerApiserver() { } // logsGardenerControllerManager prints the logfile of the gardener-controller-manager -func logsGardenerControllerManager() { +func logsGardenerControllerManager(targetReader TargetReader) { var target Target ReadTarget(pathTarget, &target) if len(target.Target) != 3 { logPodGarden("gardener-controller-manager", "garden") } else { - logPodGardenImproved("gardener-controller-manager") + logPodGardenImproved(targetReader, "gardener-controller-manager") } } @@ -546,30 +547,30 @@ func logsGardenerDashboard() { } // logsAPIServer prints the logfile of the api-server -func logsAPIServer() { - logPod("kube-apiserver", "seed", "kube-apiserver") +func logsAPIServer(targetReader TargetReader) { + logPod(targetReader, "kube-apiserver", "seed", "kube-apiserver") } -func saveLogsAPIServer() { - saveLogPod("kube-apiserver", "seed", "kube-apiserver") +func saveLogsAPIServer(targetReader TargetReader) { + saveLogPod(targetReader, "kube-apiserver", "seed", "kube-apiserver") } // logsScheduler prints the logfile of the scheduler -func logsScheduler() { - logPod("kube-scheduler", "seed", emptyString) +func logsScheduler(targetReader TargetReader) { + logPod(targetReader, "kube-scheduler", "seed", emptyString) } -func saveLogsScheduler() { - saveLogPod("kube-scheduler", "seed", emptyString) +func saveLogsScheduler(targetReader TargetReader) { + saveLogPod(targetReader, "kube-scheduler", "seed", emptyString) } // logsAPIServer prints the logfile of the controller-manager -func logsControllerManager() { - logPod("kube-controller-manager", "seed", emptyString) +func logsControllerManager(targetReader TargetReader) { + logPod(targetReader, "kube-controller-manager", "seed", emptyString) } -func saveLogsControllerManager() { - saveLogPod("kube-controller-manager", "seed", emptyString) +func saveLogsControllerManager(targetReader TargetReader) { + saveLogPod(targetReader, "kube-controller-manager", "seed", emptyString) } // logsVpnSeed prints the logfile of the vpn-seed container @@ -584,39 +585,39 @@ func logsEtcdOpertor() { } // logsEtcdMain prints the logfile of etcd-main -func logsEtcdMain(containerName string) { - logPod("etcd-main", "seed", containerName) +func logsEtcdMain(targetReader TargetReader, containerName string) { + logPod(targetReader, "etcd-main", "seed", containerName) } -func saveLogsEtcdMain(containerName string) { - saveLogPod("etcd-main", "seed", containerName) +func saveLogsEtcdMain(targetReader TargetReader, containerName string) { + saveLogPod(targetReader, "etcd-main", "seed", containerName) } // logsEtcdMainBackup prints logfiles of etcd-main-backup-sidecar pod -func logsEtcdMainBackup() { - logPod("etcd-main-backup-sidecar", "seed", emptyString) +func logsEtcdMainBackup(targetReader TargetReader) { + logPod(targetReader, "etcd-main-backup-sidecar", "seed", emptyString) } -func saveLogsEtcdMainBackup() { - saveLogPod("etcd-main-backup-sidecar", "seed", emptyString) +func saveLogsEtcdMainBackup(targetReader TargetReader) { + saveLogPod(targetReader, "etcd-main-backup-sidecar", "seed", emptyString) } // logsEtcdEvents prints the logfile of etcd-events -func logsEtcdEvents(containerName string) { - logPod("etcd-events-", "seed", containerName) +func logsEtcdEvents(targetReader TargetReader, containerName string) { + logPod(targetReader, "etcd-events-", "seed", containerName) } -func saveLogsEtcdEvents(containerName string) { - saveLogPod("etcd-events-", "seed", containerName) +func saveLogsEtcdEvents(targetReader TargetReader, containerName string) { + saveLogPod(targetReader, "etcd-events-", "seed", containerName) } // logsAddonManager prints the logfile of addon-manager -func logsAddonManager() { - logPod("addon-manager", "seed", emptyString) +func logsAddonManager(targetReader TargetReader) { + logPod(targetReader, "addon-manager", "seed", emptyString) } -func saveLogsAddonManager() { - saveLogPod("addon-manager", "seed", emptyString) +func saveLogsAddonManager(targetReader TargetReader) { + saveLogPod(targetReader, "addon-manager", "seed", emptyString) } // logsVpnShoot prints the logfile of vpn-shoot @@ -631,20 +632,20 @@ func saveLogsVpnShoot() { } // logsMachineControllerManager prints the logfile of machine-controller-manager -func logsMachineControllerManager() { - logPod("machine-controller-manager", "seed", emptyString) +func logsMachineControllerManager(targetReader TargetReader) { + logPod(targetReader, "machine-controller-manager", "seed", emptyString) } -func saveLogsMachineControllerManager() { - saveLogPod("machine-controller-manager", "seed", emptyString) +func saveLogsMachineControllerManager(targetReader TargetReader) { + saveLogPod(targetReader, "machine-controller-manager", "seed", emptyString) } // logsKubernetesDashboard prints the logfile of the dashboard -func logsKubernetesDashboard() { +func logsKubernetesDashboard(targetReader TargetReader) { var target Target ReadTarget(pathTarget, &target) namespace := "kube-system" - if len(target.Target) == 3 { + if IsTargeted(targetReader, "shoot") { var err error Client, err = clientToTarget("shoot") checkError(err) @@ -716,21 +717,21 @@ func saveLogsKubernetesDashboard() { } // logsPrometheus prints the logfiles of prometheus pod -func logsPrometheus() { - logPod("prometheus", "seed", "prometheus") +func logsPrometheus(targetReader TargetReader) { + logPod(targetReader, "prometheus", "seed", "prometheus") } -func saveLogsPrometheus() { - saveLogPod("prometheus", "seed", "prometheus") +func saveLogsPrometheus(targetReader TargetReader) { + saveLogPod(targetReader, "prometheus", "seed", "prometheus") } // logsGrafana prints the logfiles of grafana pod -func logsGrafana() { - logPod("grafana", "seed", "grafana") +func logsGrafana(targetReader TargetReader) { + logPod(targetReader, "grafana", "seed", "grafana") } -func saveLogsGrafana() { - saveLogPod("grafana", "seed", "grafana") +func saveLogsGrafana(targetReader TargetReader) { + saveLogPod(targetReader, "grafana", "seed", "grafana") } func logsGardenlet() { @@ -742,12 +743,12 @@ func saveLogsGardenlet() { } // logsClusterAutoscaler prints the logfiles of cluster-autoscaler -func logsClusterAutoscaler() { - logPod("cluster-autoscaler", "seed", "cluster-autoscaler") +func logsClusterAutoscaler(targetReader TargetReader) { + logPod(targetReader, "cluster-autoscaler", "seed", "cluster-autoscaler") } -func saveLogsClusterAutoscaler() { - saveLogPod("cluster-autoscaler", "seed", "cluster-autoscaler") +func saveLogsClusterAutoscaler(targetReader TargetReader) { + saveLogPod(targetReader, "cluster-autoscaler", "seed", "cluster-autoscaler") } // logsTerraform prints the logfiles of tf pod diff --git a/pkg/cmd/logs_test.go b/pkg/cmd/logs_test.go index de01bdeb7..03df8ea20 100644 --- a/pkg/cmd/logs_test.go +++ b/pkg/cmd/logs_test.go @@ -16,27 +16,43 @@ package cmd_test import ( "github.com/gardener/gardenctl/pkg/cmd" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + mockcmd "github.com/gardener/gardenctl/pkg/mock/cmd" + "github.com/golang/mock/gomock" "github.com/spf13/cobra" + "regexp" "strings" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" ) var _ = Describe("Logs and kubecmd command", func() { var ( - command *cobra.Command - - execute = func(command *cobra.Command, args []string) error { + ctrl *gomock.Controller + targetReader *mockcmd.MockTargetReader + command *cobra.Command + execute = func(command *cobra.Command, args []string) error { command.SetArgs(args) return command.Execute() } ) + BeforeEach(func() { + ctrl = gomock.NewController(GinkgoT()) + targetReader = mockcmd.NewMockTargetReader(ctrl) + }) + + AfterEach(func() { + ctrl.Finish() + }) + + var () + Context("with < 1 args", func() { It("should return error", func() { - command = cmd.NewLogsCmd() + command = cmd.NewLogsCmd(targetReader) err := execute(command, []string{}) Expect(err).To(HaveOccurred()) diff --git a/pkg/cmd/miscellaneous.go b/pkg/cmd/miscellaneous.go index 81fd64943..f0db64e7f 100644 --- a/pkg/cmd/miscellaneous.go +++ b/pkg/cmd/miscellaneous.go @@ -18,14 +18,12 @@ import ( "errors" "flag" "fmt" - "io" "io/ioutil" "log" "net" "net/http" "net/url" "os" - "strings" authorizationv1 "k8s.io/api/authorization/v1" @@ -165,69 +163,6 @@ func getSeedNamespaceNameForShoot(shootName string) (namespaceSeed string) { return shoot.Status.TechnicalID } -//getProjectObject returns the Project for Shoot -func getProjectObject() (*v1beta1.Project, error) { - var target Target - ReadTarget(pathTarget, &target) - Client, err := target.K8SClientToKind("garden") - checkError(err) - - if isTargeted("project") { - projectName, err := getTargetName("project") - checkError(err) - return Client.GardenerV1beta1().Projects().Get(projectName, metav1.GetOptions{}) - } else if isTargeted("seed", "shoot") { - if getRole() == "user" { - return nil, errors.New("can't determine project. Target project instead of seed") - } - seedName, err := getTargetName("seed") - checkError(err) - shootName, err := getTargetName("shoot") - checkError(err) - - shootList, err := Client.GardenerV1beta1().Shoots(metav1.NamespaceAll).List(metav1.ListOptions{ - FieldSelector: fields.SelectorFromSet( - fields.Set{ - core.ShootSeedName: seedName, - "metadata.name": shootName, - }).String(), - }) - checkError(err) - if len(shootList.Items) == 0 { - return nil, errors.New("No shoot found with name " + shootName + " that is running on the seed " + seedName) - } else if len(shootList.Items) > 1 { - return nil, errors.New("There are multiple shoots with the name " + shootName + " that are running on the seed " + seedName) - } - projectNamespace := shootList.Items[0].Namespace - - projectList, err := Client.GardenerV1beta1().Projects().List(metav1.ListOptions{}) - checkError(err) - - for _, p := range projectList.Items { - if *p.Spec.Namespace == projectNamespace { - return &p, nil - } - } - } - - return nil, errors.New("can't determine project") -} - -//getShootObject return shoot object and error -func getShootObject() (*v1beta1.Shoot, error) { - var target Target - ReadTarget(pathTarget, &target) - gardenClientset, err := target.GardenerClient() - checkError(err) - project, err := getProjectObject() - checkError(err) - shootName, err := getTargetName("shoot") - checkError(err) - shoot, err := gardenClientset.CoreV1beta1().Shoots(*project.Spec.Namespace).Get(shootName, metav1.GetOptions{}) - checkError(err) - return shoot, nil -} - // getTargetType returns error and name of type func getTargetType() (TargetKind, error) { var target Target @@ -282,28 +217,6 @@ func getGithubURL() string { return gardenConfig.GithubURL } -func capture() func() (string, error) { - r, w, err := os.Pipe() - if err != nil { - panic(err) - } - done := make(chan error, 1) - save := os.Stdout - os.Stdout = w - var buf strings.Builder - go func() { - _, err := io.Copy(&buf, r) - r.Close() - done <- err - }() - return func() (string, error) { - os.Stdout = save - w.Close() - err := <-done - return buf.String(), err - } -} - func isIPv4(host string) bool { return net.ParseIP(host) != nil && net.ParseIP(host).To4() != nil } @@ -327,10 +240,9 @@ func getPublicIP() string { } // get role either user or operator -func getRole() string { - var target Target +func getRole(targetReader TargetReader) string { var role string - ReadTarget(pathTarget, &target) + target := targetReader.ReadTarget(pathTarget) clientset, err := target.K8SClientToKind("garden") checkError(err) ssar := &authorizationv1.SelfSubjectAccessReview{ @@ -355,18 +267,18 @@ func getRole() string { getTargetMapInfo retun garden,project,seed,shoot,shootTechnicalID to global targetInfo Use `getFromTargetInfo()` instead */ -func getTargetMapInfo() { +func getTargetMapInfo(targetReader TargetReader) { + target := targetReader.ReadTarget(pathTarget) if len(targetInfo) > 0 { return } - var target Target - ReadTarget(pathTarget, &target) + for _, t := range target.Stack() { targetInfo[string(t.Kind)] = string(t.Name) } - if isTargeted("shoot") { - shoot, err := getShootObject() + if IsTargeted(targetReader, "shoot") { + shoot, err := GetTargetedShootObject(targetReader) checkError(err) targetInfo["shootTechnicalID"] = shoot.Status.TechnicalID @@ -376,32 +288,34 @@ func getTargetMapInfo() { } if targetInfo["project"] == "" { - projectObj, err := getProjectObject() + projectObj, err := GetTargetedProjectObject(targetReader) checkError(err) targetInfo["project"] = projectObj.Name } } /* -lookup Target Kind ("kind value") return Target Name "name value" from target file ~/.garden/sessions/plantingSession/target +GetTargetName lookup Target Kind ("kind value") return Target Name "name value" from target file ~/.garden/sessions/plantingSession/target */ -func getTargetName(Kind string) (string, error) { - var target Target - ReadTarget(pathTarget, &target) +func GetTargetName(targetReader TargetReader, Kind string) (string, error) { + target := targetReader.ReadTarget(pathTarget) for _, t := range target.Stack() { if string(t.Kind) == Kind { return string(t.Name), nil } } - return "", errors.New("Kind:" + Kind + "not found from ~/.garden/sessions/plantingSession/target") + return "", errors.New("Kind: " + Kind + " not found from ~/.garden/sessions/plantingSession/target") } /* -check if target Kind is exist in target file ~/.garden/sessions/plantingSession/target +IsTargeted check if target Kind is exist in target file ~/.garden/sessions/plantingSession/target */ -func isTargeted(args ...string) bool { - var target Target - ReadTarget(pathTarget, &target) +func IsTargeted(targetReader TargetReader, args ...string) bool { + target := targetReader.ReadTarget(pathTarget) + //target stack is empty return true + if len(args) == 0 && len(target.Stack()) == 0 { + return false + } targetMap := make(map[string]interface{}) for _, t := range target.Stack() { @@ -418,12 +332,123 @@ func isTargeted(args ...string) bool { return true } -//getFromTargetInfo validation value from global map targetInfo garden/project/shoot/seed/shootTechnicalID/.... -func getFromTargetInfo(key string) string { - getTargetMapInfo() +//GetFromTargetInfo validation value from global map targetInfo garden/project/shoot/seed/shootTechnicalID/.... +func GetFromTargetInfo(targetReader TargetReader, key string) string { + getTargetMapInfo(targetReader) value := targetInfo[key] if value == "" { log.Fatalf("value %s not found in targetInfo\n", key) } return value } + +// GetShootObject return shoot object and error +func GetShootObject(targetReader TargetReader, name string) (*v1beta1.Shoot, error) { + target := targetReader.ReadTarget(pathTarget) + gardenClientset, err := target.GardenerClient() + if err != nil { + return nil, err + } + project, err := GetTargetedProjectObject(targetReader) + if err != nil { + return nil, err + } + return gardenClientset.CoreV1beta1().Shoots(*project.Spec.Namespace).Get(name, metav1.GetOptions{}) +} + +//GetTargetedShootObject target Project/Seed to Shoot retrun targeted shoot object +func GetTargetedShootObject(targetReader TargetReader) (*v1beta1.Shoot, error) { + name, err := GetTargetName(targetReader, "shoot") + if err != nil { + return nil, err + } + return GetShootObject(targetReader, name) +} + +//GetTargetedProjectObject target Project/Seed to Shoot returns targeted project object +func GetTargetedProjectObject(targetReader TargetReader) (*v1beta1.Project, error) { + target := targetReader.ReadTarget(pathTarget) + Client, err := target.GardenerClient() + checkError(err) + if IsTargeted(targetReader, "project") { + name, err := GetTargetName(targetReader, "project") + checkError(err) + return GetProjectObject(targetReader, name) + } else if IsTargeted(targetReader, "seed", "shoot") { + seedName, err := GetTargetName(targetReader, "seed") + checkError(err) + shootName, err := GetTargetName(targetReader, "shoot") + checkError(err) + shootList, err := Client.CoreV1beta1().Shoots("").List(metav1.ListOptions{ + FieldSelector: fields.SelectorFromSet( + fields.Set{ + core.ShootSeedName: seedName, + "metadata.name": shootName, + }).String(), + }) + if err != nil { + return nil, err + } + if len(shootList.Items) == 0 { + return nil, errors.New("No shoot found with name " + shootName + " that is running on the seed " + seedName) + } else if len(shootList.Items) > 1 { + return nil, errors.New("There are multiple shoots with the name " + shootName + " that are running on the seed " + seedName) + } + projectNamespace := shootList.Items[0].Namespace + + projectList, err := Client.CoreV1beta1().Projects().List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + + for _, p := range projectList.Items { + if *p.Spec.Namespace == projectNamespace { + return &p, nil + } + } + } + + return nil, errors.New("can't determine project") +} + +//GetProjectObject with name option retrun project object +func GetProjectObject(targetReader TargetReader, name string) (*v1beta1.Project, error) { + target := targetReader.ReadTarget(pathTarget) + gardenClientset, err := target.GardenerClient() + if err != nil { + return nil, err + } + return gardenClientset.CoreV1beta1().Projects().Get(name, metav1.GetOptions{}) +} + +//GetSeedObject with name option retrun seed object +func GetSeedObject(targetReader TargetReader, name string) (*v1beta1.Seed, error) { + target := targetReader.ReadTarget(pathTarget) + gardenClientset, err := target.GardenerClient() + if err != nil { + return nil, err + } + return gardenClientset.CoreV1beta1().Seeds().Get(name, metav1.GetOptions{}) +} + +//GetTargetedSeedObject target Project/Seed to Shoot retrun targeted seed object +func GetTargetedSeedObject(targetReader TargetReader) (*v1beta1.Seed, error) { + var name string + var err error + if IsTargeted(targetReader, "seed") { + var err error + name, err = GetTargetName(targetReader, "seed") + if err != nil { + return nil, err + } + } else { + var shoot *v1beta1.Shoot + shoot, err = GetTargetedShootObject(targetReader) + if err != nil { + return nil, err + } + name = *shoot.Spec.SeedName + } + + return GetSeedObject(targetReader, name) +} diff --git a/pkg/cmd/miscellaneous_test.go b/pkg/cmd/miscellaneous_test.go index 91dc39018..884702cee 100644 --- a/pkg/cmd/miscellaneous_test.go +++ b/pkg/cmd/miscellaneous_test.go @@ -17,7 +17,14 @@ package cmd_test import ( "os" + "github.com/gardener/gardenctl/pkg/cmd" . "github.com/gardener/gardenctl/pkg/cmd" + mockcmd "github.com/gardener/gardenctl/pkg/mock/cmd" + gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + gardencorefake "github.com/gardener/gardener/pkg/client/core/clientset/versioned/fake" + "github.com/golang/mock/gomock" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -82,4 +89,155 @@ gardenClusters: os.Remove(pathTarget) os.Remove(pathGardenConfig) }) + + Describe("Miscellaneous", func() { + var ( + ctrl *gomock.Controller + targetReader *mockcmd.MockTargetReader + target = &cmd.Target{ + Target: []cmd.TargetMeta{ + { + Kind: cmd.TargetKindGarden, + Name: "test-garden", + }, + }, + } + ) + + BeforeEach(func() { + ctrl = gomock.NewController(GinkgoT()) + targetReader = mockcmd.NewMockTargetReader(ctrl) + }) + + AfterEach(func() { + ctrl.Finish() + }) + + Context("IsTargeted Testing", func() { + It("target seed should return False", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + Expect(IsTargeted(targetReader, "seed")).To(BeFalse()) + }) + + It("target garden should return true", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + Expect(IsTargeted(targetReader, "garden")).To(BeTrue()) + }) + + It("target empty should return true", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + Expect(IsTargeted(targetReader)).To(BeTrue()) + }) + }) + + Context("GetTargetName garden", func() { + It("should return err==nil", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target) + _, err := GetTargetName(targetReader, "garden") + Expect(err).To(BeNil()) + }) + }) + }) + + Describe("Miscellaneous", func() { + var ( + ctrl *gomock.Controller + targetReader *mockcmd.MockTargetReader + target *mockcmd.MockTargetInterface + ) + + BeforeEach(func() { + ctrl = gomock.NewController(GinkgoT()) + targetReader = mockcmd.NewMockTargetReader(ctrl) + target = mockcmd.NewMockTargetInterface(ctrl) + }) + + AfterEach(func() { + ctrl.Finish() + }) + + Context("Get Ojbect Testing", func() { + seedName := "test-seed" + nameSpace := "test-namespace" + clientSet := gardencorefake.NewSimpleClientset( + &gardencorev1beta1.Seed{ + ObjectMeta: metav1.ObjectMeta{ + Name: seedName, + }, + Spec: gardencorev1beta1.SeedSpec{ + SecretRef: &corev1.SecretReference{ + Name: "test-secret-name", + Namespace: "test-namespace", + }, + }, + }, + &gardencorev1beta1.Shoot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-shoot", + Namespace: "test-namespace", + }, + Spec: gardencorev1beta1.ShootSpec{ + SeedName: &seedName, + }, + }, + &gardencorev1beta1.ShootList{ + Items: []gardencorev1beta1.Shoot{}, + }, + &gardencorev1beta1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-project", + }, + Spec: gardencorev1beta1.ProjectSpec{ + Namespace: &nameSpace, + }, + }, + &gardencorev1beta1.ProjectList{ + Items: []gardencorev1beta1.Project{}, + }, + ) + targetMeta := []cmd.TargetMeta{ + { + Kind: cmd.TargetKindGarden, + Name: "test-garden", + }, + { + Kind: cmd.TargetKindSeed, + Name: "test-seed", + }, + { + Kind: cmd.TargetKindShoot, + Name: "test-shoot", + }, + } + + It("should pass on get Project Object", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() + target.EXPECT().GardenerClient().Return(clientSet, nil).AnyTimes() + _, err := GetProjectObject(targetReader, "test-project1") + Expect(err.Error()).To(Equal("projects.core.gardener.cloud \"test-project1\" not found")) + _, err = GetProjectObject(targetReader, "test-project") + Expect(err).NotTo(HaveOccurred()) + }) + + It("should pass on get Seed Object", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() + target.EXPECT().GardenerClient().Return(clientSet, nil).AnyTimes() + _, err := GetSeedObject(targetReader, "test-seed1") + Expect(err.Error()).To(Equal("seeds.core.gardener.cloud \"test-seed1\" not found")) + _, err = GetSeedObject(targetReader, "test-seed") + Expect(err).NotTo(HaveOccurred()) + }) + + It("should pass on get Shoot Object", func() { + targetReader.EXPECT().ReadTarget(gomock.Any()).Return(target).AnyTimes() + target.EXPECT().Stack().Return(targetMeta).AnyTimes() + target.EXPECT().GardenerClient().Return(clientSet, nil).AnyTimes() + _, err := GetShootObject(targetReader, "test-shoot1") + Expect(err.Error()).To(Equal("shoots.core.gardener.cloud \"test-shoot1\" not found")) + _, err = GetShootObject(targetReader, "test-shoot") + Expect(err).NotTo(HaveOccurred()) + }) + }) + + }) }) diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 468977525..e46659170 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -146,7 +146,7 @@ func init() { NewTargetCmd(targetReader, targetWriter, configReader, ioStreams, kubeconfigReader), NewDropCmd(targetReader, targetWriter, ioStreams), NewGetCmd(targetReader, configReader, kubeconfigReader, kubeconfigWriter, ioStreams)) - RootCmd.AddCommand(NewDownloadCmd(targetReader), NewShowCmd(targetReader), NewLogsCmd()) + RootCmd.AddCommand(NewDownloadCmd(targetReader), NewShowCmd(targetReader), NewLogsCmd(targetReader)) RootCmd.AddCommand(NewRegisterCmd(), NewUnregisterCmd()) RootCmd.AddCommand(NewCompletionCmd()) RootCmd.AddCommand(NewShellCmd(targetReader, ioStreams)) diff --git a/pkg/cmd/show.go b/pkg/cmd/show.go index e6c258128..5312f9f3b 100644 --- a/pkg/cmd/show.go +++ b/pkg/cmd/show.go @@ -248,7 +248,7 @@ func showPrometheus(targetReader TargetReader) { username, password = getMonitoringCredentials() showPod("prometheus", "seed", targetReader) KUBECONFIG := getKubeConfigOfClusterType("seed") - url, err := ExecCmdReturnOutput("kubectl", "--kubeconfig="+KUBECONFIG, "get", "ingress", "prometheus", "-n", getFromTargetInfo("shootTechnicalID"), "--no-headers", "-o", "custom-columns=:spec.rules[].host") + url, err := ExecCmdReturnOutput("kubectl", "--kubeconfig="+KUBECONFIG, "get", "ingress", "prometheus", "-n", GetFromTargetInfo(targetReader, "shootTechnicalID"), "--no-headers", "-o", "custom-columns=:spec.rules[].host") if err != nil { log.Fatalf("Cmd was unsuccessful") } @@ -316,7 +316,7 @@ func showKubernetesDashboard(targetReader TargetReader) { func showGrafana(targetReader TargetReader) { username, password = getMonitoringCredentials() showPod("grafana", "seed", targetReader) - output, err := ExecCmdReturnOutput("kubectl", "--kubeconfig="+KUBECONFIG, "get", "ingress", "grafana", "-n", getFromTargetInfo("shootTechnicalID")) + output, err := ExecCmdReturnOutput("kubectl", "--kubeconfig="+KUBECONFIG, "get", "ingress", "grafana", "-n", GetFromTargetInfo(targetReader, "shootTechnicalID")) if err != nil { log.Fatalf("Cmd was unsuccessful") } diff --git a/pkg/cmd/ssh.go b/pkg/cmd/ssh.go index ff51254da..f4d99fcb9 100644 --- a/pkg/cmd/ssh.go +++ b/pkg/cmd/ssh.go @@ -38,13 +38,13 @@ const ( ) // NewSSHCmd returns a new ssh command. -func NewSSHCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command { +func NewSSHCmd(targetReader TargetReader, ioStreams IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "ssh", Short: "SSH to a node", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - target := reader.ReadTarget(pathTarget) + target := targetReader.ReadTarget(pathTarget) if !CheckShootIsTargeted(target) { return errors.New("no shoot targeted") } @@ -53,10 +53,10 @@ func NewSSHCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command { checkError(err) if len(args) == 0 { - return printNodeNames(shoot.Name) + return printNodeNames(targetReader, shoot.Name) } - path := downloadTerraformFiles("infra", reader) + path := downloadTerraformFiles("infra", targetReader) if path != "" { path = filepath.Join(path, "terraform.tfstate") } @@ -89,13 +89,13 @@ func NewSSHCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command { infraType := shoot.Spec.Provider.Type switch infraType { case "aws": - sshToAWSNode(args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) + sshToAWSNode(targetReader, args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) case "gcp": - sshToGCPNode(args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) + sshToGCPNode(targetReader, args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) case "azure": - sshToAZNode(args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) + sshToAZNode(targetReader, args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) case "alicloud": - sshToAlicloudNode(args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) + sshToAlicloudNode(targetReader, args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) case "openstack": sshToOpenstackNode(args[0], path, user, pathSSKeypair, sshPublicKey, myPublicIP) default: @@ -119,8 +119,8 @@ func getSSHKeypair(shoot *gardencorev1beta1.Shoot) *v1.Secret { } // printNodeNames print all nodes in k8s cluster -func printNodeNames(shootName string) error { - machineList, err := getMachineList(shootName) +func printNodeNames(targetReader TargetReader, shootName string) error { + machineList, err := getMachineList(targetReader, shootName) checkError(err) fmt.Println("Nodes:") @@ -144,8 +144,8 @@ echo "gardener ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/99-gardener-user return []byte(userData) } -func getMachineList(shootName string) (*v1alpha1.MachineList, error) { - if getRole() == "user" { +func getMachineList(targetReader TargetReader, shootName string) (*v1alpha1.MachineList, error) { + if getRole(targetReader) == "user" { var target Target ReadTarget(pathTarget, &target) clientset, err := target.K8SClientToKind("shoot") diff --git a/pkg/cmd/ssh_alicloud.go b/pkg/cmd/ssh_alicloud.go index 07a425aff..aabc00502 100644 --- a/pkg/cmd/ssh_alicloud.go +++ b/pkg/cmd/ssh_alicloud.go @@ -70,10 +70,10 @@ type AliyunInstanceTypeSpec struct { } // sshToAlicloudNode provides cmds to ssh to alicloud via a public ip and clean it up afterwards. -func sshToAlicloudNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { +func sshToAlicloudNode(targetReader TargetReader, nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { // Check if this is a cleanup command if nodeName == "cleanup" { - cleanupAliyunBastionHost() + cleanupAliyunBastionHost(targetReader) return } @@ -87,7 +87,7 @@ func sshToAlicloudNode(nodeName, path, user, pathSSKeypair string, sshPublicKey a.MyPublicIP = myPublicIP + "/32" fmt.Println("") fmt.Println("(2/5) Fetching data from target shoot cluster") - a.fetchAttributes(nodeName) + a.fetchAttributes(targetReader, nodeName) fmt.Println("Data fetched from target shoot cluster.") fmt.Println("") @@ -95,7 +95,7 @@ func sshToAlicloudNode(nodeName, path, user, pathSSKeypair string, sshPublicKey a.createBastionHostSecurityGroup() fmt.Println("Bastion host security group set up.") - defer checkIsDeletionWanted(a.BastionInstanceID) + defer checkIsDeletionWanted(targetReader, a.BastionInstanceID) fmt.Println("") fmt.Println("(4/5) Setting up bastion host") @@ -119,8 +119,8 @@ func sshToAlicloudNode(nodeName, path, user, pathSSKeypair string, sshPublicKey } // fetchAttributes gets all the needed attributes for creating bastion host and its security group with given . -func (a *AliyunInstanceAttribute) fetchAttributes(nodeName string) { - a.ShootName = getFromTargetInfo("shootTechnicalID") +func (a *AliyunInstanceAttribute) fetchAttributes(targetReader TargetReader, nodeName string) { + a.ShootName = GetFromTargetInfo(targetReader, "shootTechnicalID") var err error a.InstanceID, err = fetchAlicloudInstanceIDByNodeName(nodeName) checkError(err) @@ -590,7 +590,7 @@ func (a *AliyunInstanceAttribute) getMinimumInstanceSpec() string { } //checkIsDeletionWanted checks if the user wants to delete the created IAS resources -func checkIsDeletionWanted(bastionInstanceID string) { +func checkIsDeletionWanted(targetReader TargetReader, bastionInstanceID string) { fmt.Println("Would you like to cleanup the created bastion? (y/n)") reader := bufio.NewReader(os.Stdin) @@ -600,7 +600,7 @@ func checkIsDeletionWanted(bastionInstanceID string) { switch char { case 'y', 'Y': fmt.Println("Cleanup") - cleanupAliyunBastionHost() + cleanupAliyunBastionHost(targetReader) case 'n', 'N': fmt.Println("- Run following command to hibernate bastion host:") fmt.Println("gardenctl aliyun ecs StopInstance -- --InstanceId=" + bastionInstanceID) @@ -613,7 +613,7 @@ func checkIsDeletionWanted(bastionInstanceID string) { } // cleanupAlicloudBastionHost cleans up the bastion host for the targeted cluster. -func cleanupAliyunBastionHost() { +func cleanupAliyunBastionHost(targetReader TargetReader) { fmt.Println("Cleaning up bastion host configurations...") fmt.Println("") @@ -625,7 +625,7 @@ func cleanupAliyunBastionHost() { fmt.Println("") fmt.Println("(2/4) Fetching data from target shoot cluster") - a.ShootName = getFromTargetInfo("shootTechnicalID") + a.ShootName = GetFromTargetInfo(targetReader, "shootTechnicalID") a.BastionInstanceName = a.ShootName + "-bastion" a.BastionSecurityGroupName = a.ShootName + "-bsg" fmt.Println("Data fetched from target shoot cluster.") diff --git a/pkg/cmd/ssh_aws.go b/pkg/cmd/ssh_aws.go index b12a5f275..df51d33bf 100644 --- a/pkg/cmd/ssh_aws.go +++ b/pkg/cmd/ssh_aws.go @@ -47,7 +47,7 @@ type AwsInstanceAttribute struct { } // sshToAWSNode provides cmds to ssh to aws via a bastions host and clean it up afterwards -func sshToAWSNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { +func sshToAWSNode(targetReader TargetReader, nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { a := &AwsInstanceAttribute{} a.SSHPublicKey = sshPublicKey a.MyPublicIP = myPublicIP @@ -56,7 +56,7 @@ func sshToAWSNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt fmt.Println("(1/4) Fetching data from target shoot cluster") - a.fetchAwsAttributes(nodeName, path) + a.fetchAwsAttributes(targetReader, nodeName, path) fmt.Println("Data fetched from target shoot cluster.") fmt.Println("") @@ -103,8 +103,8 @@ func sshToAWSNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt } // fetchAwsAttributes gets all the needed attributes for creating bastion host and its security group with given . -func (a *AwsInstanceAttribute) fetchAwsAttributes(nodeName, path string) { - a.ShootName = getFromTargetInfo("shootTechnicalID") +func (a *AwsInstanceAttribute) fetchAwsAttributes(targetReader TargetReader, nodeName, path string) { + a.ShootName = GetFromTargetInfo(targetReader, "shootTechnicalID") publicUtility := a.ShootName + "-public-utility-z0" arguments := fmt.Sprintf("ec2 describe-subnets --filters Name=tag:Name,Values=" + publicUtility + " --query Subnets[*].SubnetId") a.SubnetID = strings.Trim(operate("aws", arguments), "\n") diff --git a/pkg/cmd/ssh_azure.go b/pkg/cmd/ssh_azure.go index 7772dd48a..47c43ac07 100644 --- a/pkg/cmd/ssh_azure.go +++ b/pkg/cmd/ssh_azure.go @@ -36,13 +36,13 @@ type AzureInstanceAttribute struct { } // sshToAZNode provides cmds to ssh to az via a node name and clean it up afterwards -func sshToAZNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { +func sshToAZNode(targetReader TargetReader, nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { a := &AzureInstanceAttribute{} a.MyPublicIP = myPublicIP fmt.Println("") fmt.Println("(1/4) Fetching data from target shoot cluster") - a.fetchAzureAttributes(nodeName, path) + a.fetchAzureAttributes(targetReader, nodeName, path) fmt.Println("Data fetched from target shoot cluster.") fmt.Println("") @@ -89,8 +89,8 @@ func sshToAZNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byte } // fetchAttributes gets all the needed attributes for creating bastion host and its security group with given . -func (a *AzureInstanceAttribute) fetchAzureAttributes(nodeName, path string) { - a.ShootName = getFromTargetInfo("shootTechnicalID") +func (a *AzureInstanceAttribute) fetchAzureAttributes(targetReader TargetReader, nodeName, path string) { + a.ShootName = GetFromTargetInfo(targetReader, "shootTechnicalID") a.NamePublicIP = "sshIP" a.RescourceGroupName = a.ShootName diff --git a/pkg/cmd/ssh_gcp.go b/pkg/cmd/ssh_gcp.go index ba2472f34..4be74189e 100644 --- a/pkg/cmd/ssh_gcp.go +++ b/pkg/cmd/ssh_gcp.go @@ -40,14 +40,14 @@ type GCPInstanceAttribute struct { } // sshToGCPNode provides cmds to ssh to gcp via a public ip and clean it up afterwards -func sshToGCPNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { +func sshToGCPNode(targetReader TargetReader, nodeName, path, user, pathSSKeypair string, sshPublicKey []byte, myPublicIP string) { g := &GCPInstanceAttribute{} g.SSHPublicKey = sshPublicKey g.MyPublicIP = myPublicIP fmt.Println("") fmt.Println("(1/4) Fetching data from target shoot cluster") - g.fetchGCPAttributes(nodeName, path) + g.fetchGCPAttributes(targetReader, nodeName, path) fmt.Println("Data fetched from target shoot cluster.") fmt.Println("") @@ -89,8 +89,8 @@ func sshToGCPNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt } // fetchAwsAttributes gets all the needed attributes for creating bastion host and its security group with given by using gcp cli for non-operator user -func (g *GCPInstanceAttribute) fetchGCPAttributes(nodeName, path string) { - g.ShootName = getFromTargetInfo("shootTechnicalID") +func (g *GCPInstanceAttribute) fetchGCPAttributes(targetReader TargetReader, nodeName, path string) { + g.ShootName = GetFromTargetInfo(targetReader, "shootTechnicalID") g.BastionHostName = g.ShootName + "-bastions" g.FirewallRuleName = g.ShootName + "-allow-ssh-access" g.Subnetwork = g.ShootName + "-nodes" diff --git a/pkg/cmd/target.go b/pkg/cmd/target.go index 7dd6b7686..9a6f56ce1 100644 --- a/pkg/cmd/target.go +++ b/pkg/cmd/target.go @@ -248,7 +248,7 @@ func NewTargetCmd(targetReader TargetReader, targetWriter TargetWriter, configRe if len(shoots) == 0 { fmt.Println("No match for " + args[0]) } else if len(shoots) == 1 { - targetShoot(targetWriter, shoots[0], configReader) + targetShoot(targetReader, targetWriter, shoots[0], configReader) } else if len(shoots) > 1 { k8sClientToGarden, err := target.K8SClientToKind(TargetKindGarden) checkError(err) @@ -550,7 +550,7 @@ func resolveNameShoot(target TargetInterface, name string) []gardencorev1beta1.S } // targetShoot targets shoot cluster with project as default value in stack -func targetShoot(targetWriter TargetWriter, shoot gardencorev1beta1.Shoot, reader ConfigReader) { +func targetShoot(targetReader TargetReader, targetWriter TargetWriter, shoot gardencorev1beta1.Shoot, reader ConfigReader) { var target Target ReadTarget(pathTarget, &target) @@ -599,7 +599,7 @@ func targetShoot(targetWriter TargetWriter, shoot gardencorev1beta1.Shoot, reade target.Target = append(target.Target, TargetMeta{"shoot", shoot.Name}) } else if len(target.Target) == 2 { drop(targetWriter) - if target.Target[1].Kind == "seed" && getRole() != "user" { + if target.Target[1].Kind == "seed" && getRole(targetReader) != "user" { target.Target[1].Kind = "seed" target.Target[1].Name = *shoot.Spec.SeedName } else if target.Target[1].Kind == "project" { @@ -613,7 +613,7 @@ func targetShoot(targetWriter TargetWriter, shoot gardencorev1beta1.Shoot, reade } else if len(target.Target) == 3 { drop(targetWriter) drop(targetWriter) - if len(target.Target) > 2 && target.Target[1].Kind == "seed" && getRole() != "user" { + if len(target.Target) > 2 && target.Target[1].Kind == "seed" && getRole(targetReader) != "user" { target.Target = target.Target[:len(target.Target)-2] target.Target = append(target.Target, TargetMeta{"seed", *shoot.Spec.SeedName}) target.Target = append(target.Target, TargetMeta{"shoot", shoot.Name}) @@ -629,7 +629,7 @@ func targetShoot(targetWriter TargetWriter, shoot gardencorev1beta1.Shoot, reade drop(targetWriter) drop(targetWriter) drop(targetWriter) - if len(target.Target) > 3 && target.Target[1].Kind == "seed" && getRole() != "user" { + if len(target.Target) > 3 && target.Target[1].Kind == "seed" && getRole(targetReader) != "user" { target.Target = target.Target[:len(target.Target)-3] target.Target = append(target.Target, TargetMeta{"seed", *shoot.Spec.SeedName}) target.Target = append(target.Target, TargetMeta{"shoot", shoot.Name}) @@ -1096,7 +1096,7 @@ func shootWrapper(targetReader TargetReader, targetWriter TargetWriter, configRe if len(shoots) == 0 { return fmt.Errorf("no match for %q", args[1]) } else if len(shoots) == 1 { - targetShoot(targetWriter, shoots[0], configReader) + targetShoot(targetReader, targetWriter, shoots[0], configReader) } else if len(shoots) > 1 { k8sClientToGarden, err := target.K8SClientToKind(TargetKindGarden) checkError(err) diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index 1ed2e943d..22c7c817f 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -41,11 +41,9 @@ func checkError(err error) { if err != nil { if debugSwitch { _, fn, line, _ := runtime.Caller(1) - log.Printf("[error] %s:%d \n %v", fn, line, err) - os.Exit(2) + log.Fatalf("[error] %s:%d \n %v", fn, line, err) } else { - fmt.Println(err.Error()) - os.Exit(2) + log.Fatalf(err.Error()) } } }