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

add gardenctl hibernate and gardenctl wakeup to hibernate/wakeup shoot cluster #465

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ github.com/gardener/controller-manager-library v0.1.1-0.20191212112146-917449ad7
github.com/gardener/controller-manager-library v0.1.1-0.20200204110458-c263b9bb97ad/go.mod h1:v6cbldxmpL2fYBEB2lSnq3LSEPwIHus9En6iIhwNE1k=
github.com/gardener/etcd-druid v0.1.3/go.mod h1:/A8kSp7DSo7oOhsOhD9Se/xF6BOD9TQJMawf6k7AOsU=
github.com/gardener/etcd-druid v0.1.12/go.mod h1:yZrUQY9clD8/ZXK+MmEq8OS1TaKJeipV0u4kHHrwWeY=
github.com/gardener/etcd-druid v0.3.0 h1:rqOR8UPKT9tywPYowEaVAhSfYgz165whJORsijz9Tps=
github.com/gardener/etcd-druid v0.3.0/go.mod h1:uxZjZ57gIgpX554vGp495g2i8DByoS3OkVtiqsxtbwk=
github.com/gardener/external-dns-management v0.7.3/go.mod h1:Y3om11E865x4aQ7cmcHjknb8RMgCO153huRb/SvP+9o=
github.com/gardener/external-dns-management v0.7.7/go.mod h1:egCe/FPOsUbXA4WV0ne3h7nAD/nLT09hNt/FQQXK+ec=
Expand Down Expand Up @@ -882,6 +883,7 @@ k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 h1:mLmhKUm1X+pXu0zXMEzNsOF5E
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/cluster-bootstrap v0.0.0-20190918163108-da9fdfce26bb/go.mod h1:mQVbtFRxlw/BzBqBaQwIMzjDTST1KrGtzWaR4CGlsTU=
k8s.io/cluster-bootstrap v0.0.0-20191016114958-5aa5f2bc1590/go.mod h1:pmma5hMCCYfQ8Z5OyKzqHR7fKND/Pd4Z5gNGkK0AIbc=
k8s.io/cluster-bootstrap v0.16.8 h1:94hV4G7aSIkNCX2G1Jk8URz8zLermBbrrwNre2Y8S9w=
k8s.io/cluster-bootstrap v0.16.8/go.mod h1:fT1U/qWmXNmIColCsCBg4G881nWFaEqONL0xmP48rkI=
k8s.io/code-generator v0.0.0-20180823001027-3dcf91f64f63/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
Expand Down
66 changes: 66 additions & 0 deletions pkg/cmd/hibernate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"errors"
"fmt"
"os"
"time"

"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/spf13/cobra"
)

// NewHibernateCmd returns diagnostic information for a shoot.
func NewHibernateCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "hibernate",
Short: "Hibernate a cluster if wakeup-able and shoot is wakeup",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
target := reader.ReadTarget(pathTarget)
if !CheckShootIsTargeted(target) {
return errors.New("no shoot targeted")
}

shoot, err := FetchShootFromTarget(target)
checkError(err)
hibernateShoot(shoot, reader)
return nil
},
}
return cmd
}

func hibernateShoot(shoot *v1beta1.Shoot, reader TargetReader) {
if shoot.Spec.Hibernation != nil && shoot.Spec.Hibernation.Enabled != nil && *shoot.Spec.Hibernation.Enabled {
fmt.Println("shoot already hibernated!")
os.Exit(0)
}

//hibernate the shoot
newShoot := shoot.DeepCopy()
setHibernation(newShoot, true)
patchedShoot, err := MergePatchShoot(shoot, newShoot, reader)
checkError(err)
*shoot = *patchedShoot

time.Sleep(time.Second * 20)

//wait for shoot to be reconciled in 20 mins
err = waitShootReconciled(shoot, reader)
checkError(err)
}
2 changes: 2 additions & 0 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ func init() {
RootCmd.AddCommand(NewInfoCmd(targetReader, ioStreams))
RootCmd.AddCommand(NewVersionCmd(), NewUpdateCheckCmd())
RootCmd.AddCommand(NewDiagCmd(targetReader, ioStreams))
RootCmd.AddCommand(NewWakeupCmd(targetReader, ioStreams))
RootCmd.AddCommand(NewHibernateCmd(targetReader, ioStreams))

RootCmd.SuggestionsMinimumDistance = suggestionsMinimumDistance
RootCmd.BashCompletionFunction = bashCompletionFunc
Expand Down
56 changes: 56 additions & 0 deletions pkg/cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ import (
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"

gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
gardenerlogger "github.com/gardener/gardener/pkg/logger"
kutil "github.com/gardener/gardener/pkg/utils/kubernetes"
yaml "gopkg.in/yaml.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
Expand Down Expand Up @@ -370,3 +373,56 @@ func CheckIPPortReachable(ip string, port string) error {
}
return fmt.Errorf("IP %s port %s is not reachable", ip, port)
}

//setHibernation hibernate a shoot or wake up a shoot
func setHibernation(shoot *gardencorev1beta1.Shoot, hibernated bool) {
if shoot.Spec.Hibernation != nil {
shoot.Spec.Hibernation.Enabled = &hibernated
}
shoot.Spec.Hibernation = &gardencorev1beta1.Hibernation{
Enabled: &hibernated,
}
Comment on lines +382 to +384
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will overwrite hibernation schedule.

}

//waitShootReconciled wait for the shoot to be reconciled successfully
func waitShootReconciled(shoot *gardencorev1beta1.Shoot, targetReader TargetReader) error {
attemptCnt := 0
fmt.Println("Shoot is being reconciled, the progress will be updated 1 min interval")
for attemptCnt < 20 {
target := targetReader.ReadTarget(pathTarget)
gardenClientset, err := target.GardenerClient()
checkError(err)
newShoot, err := gardenClientset.CoreV1beta1().Shoots(shoot.GetNamespace()).Get(shoot.Name, metav1.GetOptions{})
checkError(err)
if newShoot.Status.LastOperation.State == "Succeeded" &&
newShoot.Status.LastOperation.Type == "Reconcile" &&
newShoot.Status.LastOperation.Progress == 100 {
fmt.Println("Shoot has been successfully reconciled")
return nil
}
fmt.Println("Last operation state is " + newShoot.Status.LastOperation.State)
fmt.Println("Last operation type is " + newShoot.Status.LastOperation.Type)
fmt.Println("Last operation progress is " + strconv.Itoa(int(newShoot.Status.LastOperation.Progress)) + "%")
time.Sleep(time.Second * 60)
attemptCnt++
}
return fmt.Errorf("Shoot not successfully reconciled within 20 mins")

}

//MergePatchShoot Merge old shoot with new shoot
func MergePatchShoot(oldShoot, newShoot *gardencorev1beta1.Shoot, targetReader TargetReader) (*gardencorev1beta1.Shoot, error) {
patchBytes, err := kutil.CreateTwoWayMergePatch(oldShoot, newShoot)
if err != nil {
return nil, fmt.Errorf("failed to patch bytes")
}

target := targetReader.ReadTarget(pathTarget)
gardenClientset, err := target.GardenerClient()
checkError(err)
patchedShoot, err := gardenClientset.CoreV1beta1().Shoots(oldShoot.GetNamespace()).Patch(oldShoot.GetName(), types.StrategicMergePatchType, patchBytes)
if err == nil {
*oldShoot = *patchedShoot
}
return patchedShoot, err
}
66 changes: 66 additions & 0 deletions pkg/cmd/wakeup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"errors"
"fmt"
"os"
"time"

"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/spf13/cobra"
)

// NewWakeupCmd returns diagnostic information for a shoot.
func NewWakeupCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "wakeup",
Short: "Wake up a cluster if wakeup-able and shoot is hibernated",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
target := reader.ReadTarget(pathTarget)
if !CheckShootIsTargeted(target) {
return errors.New("no shoot targeted")
}

shoot, err := FetchShootFromTarget(target)
checkError(err)
wakeupShoot(shoot, reader)
return nil
},
}
return cmd
}

func wakeupShoot(shoot *v1beta1.Shoot, reader TargetReader) {
if shoot.Spec.Hibernation == nil || shoot.Spec.Hibernation.Enabled == nil || !*shoot.Spec.Hibernation.Enabled {
fmt.Println("Shoot already wakeup")
os.Exit(0)
}

//wakeup the shoot
newShoot := shoot.DeepCopy()
setHibernation(newShoot, false)
patchedShoot, err := MergePatchShoot(shoot, newShoot, reader)
checkError(err)
*shoot = *patchedShoot

time.Sleep(time.Second * 20)

//wait for shoot to be reconciled in 20 mins
err = waitShootReconciled(shoot, reader)
checkError(err)
}
Loading