Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Commit

Permalink
Show hibernated shoots
Browse files Browse the repository at this point in the history
  • Loading branch information
Kristian-ZH authored and ialidzhikov committed Nov 21, 2019
1 parent cab6423 commit b4d0c46
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 99 deletions.
6 changes: 3 additions & 3 deletions .ci/pipeline_definitions
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ gardenctl:
inject_effective_version: true
steps:
check:
image: 'golang:1.13.3'
image: 'golang:1.13.4'
test:
image: 'golang:1.13.3'
image: 'golang:1.13.4'
build:
image: 'golang:1.13.3'
image: 'golang:1.13.4'
jobs:
head-update:
traits:
Expand Down
28 changes: 21 additions & 7 deletions pkg/cmd/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,42 @@ func NewInfoCmd(targetReader TargetReader, ioStreams IOStreams) *cobra.Command {
return err
}

var unscheduled = 0
shootsCountPerSeed := make(map[string]int)
var (
unscheduled = 0
hibernatedShootsCount = 0
totalShootsCountPerSeed = make(map[string]int)
hibernatedShootsCountPerSeed = make(map[string]int)
)

for _, shoot := range shootList.Items {
if shoot.Spec.SeedName == nil {
unscheduled++
continue
}
shootsCountPerSeed[*shoot.Spec.SeedName]++
totalShootsCountPerSeed[*shoot.Spec.SeedName]++
if shoot.Status.IsHibernated {
hibernatedShootsCountPerSeed[*shoot.Spec.SeedName]++
hibernatedShootsCount++
}
}

var sortedSeeds []string
for seed := range shootsCountPerSeed {
for seed := range totalShootsCountPerSeed {
sortedSeeds = append(sortedSeeds, seed)
}
sort.Strings(sortedSeeds)

fmt.Fprintf(ioStreams.Out, "Garden: %s\n", targetStack[0].Name)
fmt.Fprintf(ioStreams.Out, "Shoots:\n")
fmt.Fprintf(ioStreams.Out, "\ttotal: %d\n", len(shootList.Items))
fmt.Fprintf(ioStreams.Out, "\tunscheduled: %d\n", unscheduled)
fmt.Fprintf(ioStreams.Out, " active: %d\n", len(shootList.Items)-hibernatedShootsCount-unscheduled)
fmt.Fprintf(ioStreams.Out, " hibernated: %d\n", hibernatedShootsCount)
fmt.Fprintf(ioStreams.Out, " unscheduled: %d\n", unscheduled)
fmt.Fprintf(ioStreams.Out, " total: %d\n\n", len(shootList.Items))
for _, seed := range sortedSeeds {
fmt.Fprintf(ioStreams.Out, "\t%s: %d\n", seed, shootsCountPerSeed[seed])
fmt.Fprintf(ioStreams.Out, " %s:\n", seed)
fmt.Fprintf(ioStreams.Out, " active: %d\n", totalShootsCountPerSeed[seed]-hibernatedShootsCountPerSeed[seed])
fmt.Fprintf(ioStreams.Out, " hibernated: %d\n", hibernatedShootsCountPerSeed[seed])
fmt.Fprintf(ioStreams.Out, " total: %d\n", totalShootsCountPerSeed[seed])
}

return nil
Expand Down
8 changes: 5 additions & 3 deletions pkg/cmd/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ var _ = Describe("Info command", func() {

actual := out.String()
Expect(actual).To(ContainSubstring("Garden: prod"))
Expect(actual).To(ContainSubstring("total: 3"))
Expect(actual).To(ContainSubstring("active: 2"))
Expect(actual).To(ContainSubstring("hibernated: 0"))
Expect(actual).To(ContainSubstring("unscheduled: 1"))
Expect(actual).To(ContainSubstring("aws: 1"))
Expect(actual).To(ContainSubstring("gcp: 1"))
Expect(actual).To(ContainSubstring("total: 3"))
Expect(actual).To(ContainSubstring("aws:\n active: 1\n hibernated: 0\n total: 1"))
Expect(actual).To(ContainSubstring("gcp:\n active: 1\n hibernated: 0\n total: 1"))
})
})
})
18 changes: 15 additions & 3 deletions pkg/cmd/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ func getProjectsWithShoots(ioStreams IOStreams) {
var pm ProjectMeta
for _, shoot := range shootList.Items {
if shoot.Namespace == *project.Spec.Namespace {
pm.Shoots = append(pm.Shoots, shoot.Name)
currentShoot := shoot.Name
if shoot.Status.IsHibernated {
currentShoot += " (Hibernated)"
}
pm.Shoots = append(pm.Shoots, currentShoot)
}
}
pm.Project = project.Name
Expand Down Expand Up @@ -173,7 +177,11 @@ func getProjectsWithShootsForSeed(ioStreams IOStreams) {
var pm ProjectMeta
for _, shoot := range shootList.Items {
if shoot.Namespace == *project.Spec.Namespace && target.Target[1].Name == *shoot.Spec.SeedName {
pm.Shoots = append(pm.Shoots, shoot.Name)
currentShoot := shoot.Name
if shoot.Status.IsHibernated {
currentShoot += " (Hibernated)"
}
pm.Shoots = append(pm.Shoots, currentShoot)
}
}
if len(pm.Shoots) > 0 {
Expand Down Expand Up @@ -303,7 +311,11 @@ func getSeedsWithShootsForProject(ioStreams IOStreams) {
for _, shoot := range shootList.Items {
for index, seed := range seeds.Seeds {
if seed.Seed == *shoot.Spec.SeedName {
seeds.Seeds[index].Shoots = append(seeds.Seeds[index].Shoots, shoot.Name)
currentShoot := shoot.Name
if shoot.Status.IsHibernated {
currentShoot += " (Hibernated)"
}
seeds.Seeds[index].Shoots = append(seeds.Seeds[index].Shoots, currentShoot)
}
}
}
Expand Down
1 change: 0 additions & 1 deletion pkg/cmd/miscellaneous.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ func clientToTarget(target TargetKind) (*k8s.Clientset, error) {
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
masterURL = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
flag.Parse()
} else {
err := flag.Set("kubeconfig", KUBECONFIG)
Expand Down
17 changes: 16 additions & 1 deletion pkg/cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"time"

gardencorev1alpha1 "github.com/gardener/gardener/pkg/apis/core/v1alpha1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -55,10 +56,24 @@ func NewShellCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command {
return errors.New("project targeted")
}

var shoot *gardencorev1alpha1.Shoot
if len(target.Stack()) == 1 {
return errors.New("garden cluster targeted")
} else if len(target.Stack()) > 2 {
if shoot, err = FetchShootFromTarget(target); err != nil {
return err
}
}

if shoot != nil && shoot.Status.IsHibernated {
return fmt.Errorf("shoot %q is hibernated", shoot.Name)
}

var client kubernetes.Interface
if client, err = target.K8SClient(); err != nil {
return err
}

if len(args) == 0 {
return printNodes(client, ioStreams)
}
Expand All @@ -78,7 +93,7 @@ func printNodes(client kubernetes.Interface, ioStreams IOStreams) (err error) {
if nodes, err = client.CoreV1().Nodes().List(metav1.ListOptions{}); err != nil {
return err
}

fmt.Fprintln(ioStreams.Out, "Node names:")
for _, n := range nodes.Items {
fmt.Fprintln(ioStreams.Out, n.Name)
}
Expand Down
103 changes: 85 additions & 18 deletions pkg/cmd/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package cmd_test
import (
"github.com/gardener/gardenctl/pkg/cmd"
mockcmd "github.com/gardener/gardenctl/pkg/mock/cmd"
gardencorev1alpha1 "github.com/gardener/gardener/pkg/apis/core/v1alpha1"
gardencorefake "github.com/gardener/gardener/pkg/client/core/clientset/versioned/fake"
"github.com/golang/mock/gomock"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
Expand All @@ -30,16 +32,53 @@ import (
var _ = Describe("Shell command", func() {

var (
ctrl *gomock.Controller
reader *mockcmd.MockTargetReader
target *mockcmd.MockTargetInterface
clientSet *fake.Clientset
command *cobra.Command
ctrl *gomock.Controller
reader *mockcmd.MockTargetReader
target *mockcmd.MockTargetInterface
command *cobra.Command

execute = func(command *cobra.Command, args []string) error {
command.SetArgs(args)
return command.Execute()
}

seedName = "test-name"
shootName = "test-shoot"

createGardenClientSet = func(isHibernated bool) *gardencorefake.Clientset {
return gardencorefake.NewSimpleClientset(
&gardencorev1alpha1.Seed{
ObjectMeta: metav1.ObjectMeta{
Name: seedName,
},
},
&gardencorev1alpha1.Shoot{
ObjectMeta: metav1.ObjectMeta{
Name: shootName,
},
Spec: gardencorev1alpha1.ShootSpec{
SeedName: &seedName,
},
Status: gardencorev1alpha1.ShootStatus{
IsHibernated: isHibernated,
},
})
}

targetMeta = []cmd.TargetMeta{
{
Kind: cmd.TargetKindGarden,
Name: "test-garden",
},
{
Kind: cmd.TargetKindSeed,
Name: seedName,
},
{
Kind: cmd.TargetKindShoot,
Name: shootName,
},
}
)

BeforeEach(func() {
Expand All @@ -54,27 +93,31 @@ var _ = Describe("Shell command", func() {

Context("without args", func() {
It("should list the node names", func() {
clientSet = fake.NewSimpleClientset(&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "minikube",
},
})
gardenClientSet := createGardenClientSet(false)
k8sClientSet := fake.NewSimpleClientset(
&corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "minikube"}},
)

reader.EXPECT().ReadTarget(gomock.Any()).Return(target)
target.EXPECT().Kind().Return(cmd.TargetKindShoot, nil)
target.EXPECT().K8SClient().Return(clientSet, nil)
target.EXPECT().Stack().Return(targetMeta).AnyTimes()
target.EXPECT().GardenerClient().Return(gardenClientSet, nil)
target.EXPECT().K8SClient().Return(k8sClientSet, nil)

ioStreams, _, out, _ := cmd.NewTestIOStreams()
command = cmd.NewShellCmd(reader, ioStreams)
err := execute(command, []string{})

Expect(err).NotTo(HaveOccurred())
Expect(out.String()).To(Equal("minikube\n"))
Expect(out.String()).To(Equal("Node names:\nminikube\n"))
})

Context("when project is targeted", func() {
It("should return error", func() {
reader.EXPECT().ReadTarget(gomock.Any()).Return(target)
target.EXPECT().Kind().Return(cmd.TargetKindProject, nil)
gomock.InOrder(
reader.EXPECT().ReadTarget(gomock.Any()).Return(target),
target.EXPECT().Kind().Return(cmd.TargetKindProject, nil),
)

ioStreams, _, _, _ := cmd.NewTestIOStreams()
command = cmd.NewShellCmd(reader, ioStreams)
Expand All @@ -88,10 +131,14 @@ var _ = Describe("Shell command", func() {

Context("with non-existing node name", func() {
It("should return error", func() {
clientSet = fake.NewSimpleClientset()
gardenClientSet := createGardenClientSet(false)
k8sClientSet := fake.NewSimpleClientset()

reader.EXPECT().ReadTarget(gomock.Any()).Return(target)
target.EXPECT().Kind().Return(cmd.TargetKindShoot, nil)
target.EXPECT().K8SClient().Return(clientSet, nil)
target.EXPECT().K8SClient().Return(k8sClientSet, nil)
target.EXPECT().GardenerClient().Return(gardenClientSet, nil)
target.EXPECT().Stack().Return(targetMeta).AnyTimes()

ioStreams, _, _, _ := cmd.NewTestIOStreams()
command = cmd.NewShellCmd(reader, ioStreams)
Expand All @@ -104,8 +151,10 @@ var _ = Describe("Shell command", func() {

Context("when project is targeted", func() {
It("should return error", func() {
reader.EXPECT().ReadTarget(gomock.Any()).Return(target)
target.EXPECT().Kind().Return(cmd.TargetKindProject, nil)
gomock.InOrder(
reader.EXPECT().ReadTarget(gomock.Any()).Return(target),
target.EXPECT().Kind().Return(cmd.TargetKindProject, nil),
)

ioStreams, _, _, _ := cmd.NewTestIOStreams()
command = cmd.NewShellCmd(reader, ioStreams)
Expand All @@ -126,4 +175,22 @@ var _ = Describe("Shell command", func() {
Expect(err.Error()).To(Equal("command must be in the format: gardenctl shell (node|pod)"))
})
})

Context("with hibernated shoot", func() {
It("should not list nodes", func() {
gardenClientSet := createGardenClientSet(true)

reader.EXPECT().ReadTarget(gomock.Any()).Return(target)
target.EXPECT().Kind().Return(cmd.TargetKindShoot, nil)
target.EXPECT().GardenerClient().Return(gardenClientSet, nil)
target.EXPECT().Stack().Return(targetMeta).AnyTimes()

ioStreams, _, _, _ := cmd.NewTestIOStreams()
command = cmd.NewShellCmd(reader, ioStreams)
err := execute(command, []string{})

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("shoot \"test-shoot\" is hibernated"))
})
})
})
Loading

0 comments on commit b4d0c46

Please sign in to comment.