github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/pkg/kubernetes/client.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"reflect"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	"k8s.io/apimachinery/pkg/api/meta"
    18  
    19  	extensions "k8s.io/api/extensions/v1beta1"
    20  	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
    21  	k8syaml "sigs.k8s.io/yaml"
    22  
    23  	"github.com/caos/orbos/pkg/labels"
    24  
    25  	"github.com/caos/orbos/internal/helpers"
    26  	"github.com/caos/orbos/mntr"
    27  	apps "k8s.io/api/apps/v1"
    28  	batch "k8s.io/api/batch/v1"
    29  	"k8s.io/api/batch/v1beta1"
    30  	batchv1beta1 "k8s.io/api/batch/v1beta1"
    31  	core "k8s.io/api/core/v1"
    32  	policy "k8s.io/api/policy/v1beta1"
    33  	rbac "k8s.io/api/rbac/v1"
    34  	apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
    35  	apixv1beta1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
    36  	macherrs "k8s.io/apimachinery/pkg/api/errors"
    37  	mach "k8s.io/apimachinery/pkg/apis/meta/v1"
    38  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    39  	"k8s.io/apimachinery/pkg/runtime/schema"
    40  	"k8s.io/apimachinery/pkg/types"
    41  	"k8s.io/apimachinery/pkg/watch"
    42  	"k8s.io/client-go/discovery"
    43  	"k8s.io/client-go/discovery/cached/memory"
    44  	"k8s.io/client-go/dynamic"
    45  	"k8s.io/client-go/kubernetes"
    46  	"k8s.io/client-go/kubernetes/scheme"
    47  	clgocore "k8s.io/client-go/kubernetes/typed/core/v1"
    48  	"k8s.io/client-go/rest"
    49  	"k8s.io/client-go/restmapper"
    50  	"k8s.io/client-go/tools/clientcmd"
    51  	"k8s.io/client-go/tools/remotecommand"
    52  )
    53  
    54  const TaintKeyPrefix = "node.orbos.ch/"
    55  
    56  type NodeWithKubeadm interface {
    57  	Execute(stdin io.Reader, cmd string) ([]byte, error)
    58  }
    59  
    60  type IDFunc func() string
    61  
    62  func (i IDFunc) ID() string {
    63  	return i()
    64  }
    65  
    66  type ClientInt interface {
    67  	ApplyService(rsc *core.Service) error
    68  	DeleteService(namespace, name string) error
    69  
    70  	GetJob(namespace, name string) (*batch.Job, error)
    71  	ApplyJob(rsc *batch.Job) error
    72  	ApplyJobDryRun(rsc *batch.Job) error
    73  	DeleteJob(namespace string, name string) error
    74  	WaitUntilJobCompleted(namespace string, name string, timeout time.Duration) error
    75  
    76  	ApplyServiceAccount(rsc *core.ServiceAccount) error
    77  	DeleteServiceAccount(namespace, name string) error
    78  
    79  	ApplyStatefulSet(rsc *apps.StatefulSet, force bool) error
    80  	DeleteStatefulset(namespace, name string) error
    81  	ScaleStatefulset(namespace, name string, replicaCount int) error
    82  	WaitUntilStatefulsetIsReady(namespace string, name string, containerCheck, readyCheck bool, timeout time.Duration) error
    83  
    84  	ExecInPodWithOutput(namespace, name, container, command string) (string, error)
    85  	ExecInPod(namespace, name, container, command string) error
    86  
    87  	GetDeployment(namespace, name string) (*apps.Deployment, error)
    88  	ApplyDeployment(rsc *apps.Deployment, force bool) error
    89  	DeleteDeployment(namespace, name string) error
    90  	PatchDeployment(namespace, name string, data string) error
    91  	WaitUntilDeploymentReady(namespace string, name string, containerCheck, readyCheck bool, timeout time.Duration) error
    92  	ScaleDeployment(namespace, name string, replicaCount int) error
    93  	ExecInPodOfDeployment(namespace, name, container, command string) error
    94  
    95  	CheckCRD(name string) (*apixv1beta1.CustomResourceDefinition, bool, error)
    96  	GetNamespacedCRDResource(group, version, kind, namespace, name string) (*unstructured.Unstructured, error)
    97  	ApplyNamespacedCRDResource(group, version, kind, namespace, name string, crd *unstructured.Unstructured) error
    98  	DeleteNamespacedCRDResource(group, version, kind, namespace, name string) error
    99  	ApplyCRDResource(crd *unstructured.Unstructured) error
   100  	DeleteCRDResource(group, version, kind, name string) error
   101  
   102  	ApplyCronJob(rsc *v1beta1.CronJob) error
   103  	DeleteCronJob(namespace string, name string) error
   104  	ListCronJobs(namespace string, labels map[string]string) (*batchv1beta1.CronJobList, error)
   105  
   106  	ListSecrets(namespace string, labels map[string]string) (*core.SecretList, error)
   107  	GetSecret(namespace string, name string) (*core.Secret, error)
   108  	ApplySecret(rsc *core.Secret) error
   109  	DeleteSecret(namespace, name string) error
   110  	WaitForSecret(namespace string, name string, timeout time.Duration) error
   111  
   112  	GetConfigMap(namespace, name string) (*core.ConfigMap, error)
   113  	ApplyConfigmap(rsc *core.ConfigMap) error
   114  	DeleteConfigmap(namespace, name string) error
   115  	WaitForConfigMap(namespace string, name string, timeout time.Duration) error
   116  
   117  	ApplyRole(rsc *rbac.Role) error
   118  	DeleteRole(namespace, name string) error
   119  
   120  	ApplyClusterRole(rsc *rbac.ClusterRole) error
   121  	DeleteClusterRole(name string) error
   122  
   123  	ApplyIngress(rsc *extensions.Ingress) error
   124  	DeleteIngress(namespace, name string) error
   125  
   126  	ApplyRoleBinding(rsc *rbac.RoleBinding) error
   127  	DeleteRoleBinding(namespace, name string) error
   128  
   129  	ApplyClusterRoleBinding(rsc *rbac.ClusterRoleBinding) error
   130  	DeleteClusterRoleBinding(name string) error
   131  
   132  	ApplyPodDisruptionBudget(rsc *policy.PodDisruptionBudget) error
   133  	DeletePodDisruptionBudget(namespace string, name string) error
   134  
   135  	ApplyNamespace(rsc *core.Namespace) error
   136  	DeleteNamespace(name string) error
   137  
   138  	ListPersistentVolumes() (*core.PersistentVolumeList, error)
   139  
   140  	ApplyPlainYAML(mntr.Monitor, []byte) error
   141  
   142  	ListPersistentVolumeClaims(namespace string) (*core.PersistentVolumeClaimList, error)
   143  	DeletePersistentVolumeClaim(namespace, name string, timeout time.Duration) error
   144  }
   145  
   146  var _ ClientInt = (*Client)(nil)
   147  
   148  type Client struct {
   149  	monitor           mntr.Monitor
   150  	set               *kubernetes.Clientset
   151  	dynamic           dynamic.Interface
   152  	apixv1beta1client *apixv1beta1client.ApiextensionsV1beta1Client
   153  	mapper            *restmapper.DeferredDiscoveryRESTMapper
   154  	restConfig        *rest.Config
   155  	available         bool
   156  }
   157  
   158  func NewK8sClientPathBeforeInCluster(monitor mntr.Monitor, kubeconfigPath string) (*Client, error) {
   159  	kubeconfigStr := ""
   160  	if kubeconfigPath != "" {
   161  		value, err := ioutil.ReadFile(helpers.PruneHome(kubeconfigPath))
   162  		if err == nil {
   163  			kubeconfigStr = string(value)
   164  		}
   165  	}
   166  
   167  	return NewK8sClient(monitor, &kubeconfigStr, kubeconfigPath)
   168  }
   169  
   170  func newClient(monitor mntr.Monitor) *Client {
   171  	return &Client{monitor: monitor}
   172  }
   173  
   174  func NewK8sClient(monitor mntr.Monitor, kubeconfig *string, kubeconfigPath string) (*Client, error) {
   175  	kc := newClient(monitor)
   176  	if err := kc.init(kubeconfig, kubeconfigPath); err != nil {
   177  		return nil, err
   178  	}
   179  	return kc, nil
   180  }
   181  
   182  func NewK8sClientWithConfig(monitor mntr.Monitor, conf *rest.Config) (*Client, error) {
   183  	kc := newClient(monitor)
   184  	if err := kc.initConfig(conf); err != nil {
   185  		return nil, err
   186  	}
   187  	return kc, nil
   188  }
   189  
   190  func (c *Client) checkConnectivity() error {
   191  
   192  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   193  	defer cancel()
   194  	_, err := c.set.CoreV1().Nodes().Get(ctx, "<<<-impo$$ible->>>", mach.GetOptions{})
   195  	if err == nil || macherrs.IsNotFound(err) {
   196  		return nil
   197  	}
   198  	return fmt.Errorf("connectivity check get node failed: %w", err)
   199  }
   200  
   201  func (c *Client) nodeApi() clgocore.NodeInterface {
   202  	return c.set.CoreV1().Nodes()
   203  }
   204  
   205  type File struct {
   206  	Name    string
   207  	Content []byte
   208  }
   209  
   210  func (c *Client) ApplyNamespace(rsc *core.Namespace) error {
   211  	resources := c.set.CoreV1().Namespaces()
   212  	return c.applyResource("namespace", rsc.GetName(), func() error {
   213  
   214  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   215  		return err
   216  	}, func() error {
   217  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   218  		return err
   219  	})
   220  }
   221  func (c *Client) DeleteNamespace(name string) error {
   222  	return notFoundIsSuccess(c.set.CoreV1().Namespaces().Delete(context.Background(), name, mach.DeleteOptions{}))
   223  }
   224  
   225  func (c *Client) ListNamespaces() (*core.NamespaceList, error) {
   226  	return c.set.CoreV1().Namespaces().List(context.Background(), mach.ListOptions{})
   227  }
   228  
   229  func (c *Client) ListSecrets(namespace string, labels map[string]string) (*core.SecretList, error) {
   230  	labelSelector := ""
   231  	for k, v := range labels {
   232  		if labelSelector == "" {
   233  			labelSelector = fmt.Sprintf("%s=%s", k, v)
   234  		} else {
   235  			labelSelector = fmt.Sprintf("%s, %s=%s", labelSelector, k, v)
   236  		}
   237  	}
   238  
   239  	return c.set.CoreV1().Secrets(namespace).List(context.Background(), mach.ListOptions{LabelSelector: labelSelector})
   240  }
   241  
   242  func (c *Client) ListPersistentVolumes() (*core.PersistentVolumeList, error) {
   243  	return c.set.CoreV1().PersistentVolumes().List(context.Background(), mach.ListOptions{})
   244  }
   245  
   246  func (c *Client) ListPersistentVolumeClaims(namespace string) (*core.PersistentVolumeClaimList, error) {
   247  	return c.set.CoreV1().PersistentVolumeClaims(namespace).List(context.Background(), mach.ListOptions{})
   248  }
   249  
   250  func (c *Client) DeletePersistentVolumeClaim(namespace, name string, timeout time.Duration) error {
   251  	ctx := context.Background()
   252  
   253  	returnChannel := make(chan error, 1)
   254  	interval := time.Second * 1
   255  	timesS := (timeout / interval) * time.Second
   256  
   257  	go func() {
   258  		ctx := context.Background()
   259  		for i := 0; i < int(timesS.Seconds()); i++ {
   260  			_, err := c.set.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, mach.GetOptions{})
   261  			if err != nil && !macherrs.IsNotFound(err) {
   262  				returnChannel <- err
   263  				return
   264  			}
   265  
   266  			if macherrs.IsNotFound(err) {
   267  				returnChannel <- nil
   268  				return
   269  			}
   270  			time.Sleep(interval)
   271  		}
   272  		returnChannel <- errors.New("delete pvc timeout")
   273  		return
   274  	}()
   275  
   276  	if err := notFoundIsSuccess(c.set.CoreV1().PersistentVolumeClaims(namespace).Delete(ctx, name, mach.DeleteOptions{})); err != nil {
   277  		return err
   278  	}
   279  
   280  	select {
   281  	case res := <-returnChannel:
   282  		return res
   283  	case <-time.After(timeout):
   284  		return errors.New("timeout while waiting for job to complete")
   285  	}
   286  }
   287  
   288  func (c *Client) ScaleDeployment(namespace, name string, replicaCount int) error {
   289  	patch := []byte(`{"spec":{"replicas":` + strconv.Itoa(replicaCount) + `}}`)
   290  	_, err := c.set.AppsV1().Deployments(namespace).Patch(context.Background(), name, types.StrategicMergePatchType, patch, mach.PatchOptions{})
   291  	return err
   292  }
   293  
   294  func (c *Client) GetDeployment(namespace, name string) (*apps.Deployment, error) {
   295  	return c.set.AppsV1().Deployments(namespace).Get(context.Background(), name, mach.GetOptions{})
   296  }
   297  
   298  func (c *Client) ApplyDeployment(rsc *apps.Deployment, force bool) error {
   299  	resources := c.set.AppsV1().Deployments(rsc.GetNamespace())
   300  	rscLabels, err := labels.NameFrom(rsc.Labels)
   301  	if err != nil {
   302  		return err
   303  	}
   304  
   305  	rscSelector, err := labels.SelectorFrom(rsc.Spec.Selector.MatchLabels)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	return c.applyController(
   311  		"deployment",
   312  		force,
   313  		rscLabels,
   314  		rscSelector,
   315  		func() (*labels.Selector, error) {
   316  			sts, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   317  			if err != nil {
   318  				return nil, err
   319  			}
   320  
   321  			return labels.SelectorFrom(sts.Spec.Selector.MatchLabels)
   322  		},
   323  		func() error {
   324  			_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   325  			return err
   326  		},
   327  		func() error {
   328  			_, err = resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   329  			return err
   330  		},
   331  		func() error {
   332  			return c.DeleteDeployment(rsc.GetNamespace(), rsc.GetName())
   333  		},
   334  	)
   335  }
   336  
   337  func (c *Client) DeleteDeployment(namespace, name string) error {
   338  	return notFoundIsSuccess(c.set.AppsV1().Deployments(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   339  }
   340  func (c *Client) PatchDeployment(namespace, name string, data string) error {
   341  	_, err := c.set.AppsV1().Deployments(namespace).Patch(context.Background(), name, types.StrategicMergePatchType, []byte(data), mach.PatchOptions{})
   342  	return err
   343  }
   344  
   345  func (c *Client) WaitUntilDeploymentReady(namespace string, name string, containerCheck, readyCheck bool, timeout time.Duration) error {
   346  	returnChannel := make(chan error, 1)
   347  	go func() {
   348  		ctx := context.Background()
   349  		deploy, err := c.set.AppsV1().Deployments(namespace).Get(ctx, name, mach.GetOptions{})
   350  		if err != nil {
   351  			returnChannel <- err
   352  			return
   353  		}
   354  
   355  		labelSelector := getLabelSelector(deploy.Spec.Selector.MatchLabels)
   356  
   357  		watch, err := c.set.CoreV1().Pods(namespace).Watch(ctx, mach.ListOptions{
   358  			LabelSelector: labelSelector,
   359  		})
   360  		defer watch.Stop()
   361  		if err != nil {
   362  			returnChannel <- err
   363  			return
   364  		}
   365  		replicas := deploy.Spec.Replicas
   366  
   367  		returnChannel <- waitForPodsPhase(watch, int(*replicas), core.PodRunning, containerCheck, readyCheck)
   368  	}()
   369  
   370  	select {
   371  	case res := <-returnChannel:
   372  		return res
   373  	case <-time.After(timeout):
   374  		return errors.New("timeout while waiting for deployment to be ready")
   375  	}
   376  }
   377  
   378  func (c *Client) ApplyService(rsc *core.Service) error {
   379  	resources := c.set.CoreV1().Services(rsc.GetNamespace())
   380  	return c.applyResource("service", rsc.GetName(), func() error {
   381  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   382  		return err
   383  	}, func() error {
   384  		svc, err := resources.Get(context.Background(), rsc.Name, mach.GetOptions{})
   385  		if err != nil {
   386  			return err
   387  		}
   388  		rsc.Spec.ClusterIP = svc.Spec.ClusterIP
   389  		rsc.ObjectMeta.ResourceVersion = svc.ObjectMeta.ResourceVersion
   390  		_, err = resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   391  		return err
   392  	})
   393  }
   394  
   395  func (c *Client) DeleteService(namespace, name string) error {
   396  	return notFoundIsSuccess(c.set.CoreV1().Services(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   397  }
   398  
   399  func (c *Client) GetJob(namespace, name string) (*batch.Job, error) {
   400  	return c.set.BatchV1().Jobs(namespace).Get(context.Background(), name, mach.GetOptions{})
   401  }
   402  
   403  func (c *Client) ApplyJobDryRun(rsc *batch.Job) error {
   404  	resources := c.set.BatchV1().Jobs(rsc.Namespace)
   405  	return c.applyResource("job", rsc.GetName(), func() error {
   406  		res, err := resources.Create(context.Background(), rsc, mach.CreateOptions{DryRun: []string{mach.DryRunAll}})
   407  		if err != nil {
   408  			return err
   409  		}
   410  		*rsc = *res
   411  		return nil
   412  	}, func() error {
   413  		j, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   414  		if err != nil {
   415  			return err
   416  		}
   417  		if j.GetName() == rsc.GetName() && j.GetNamespace() == rsc.GetNamespace() {
   418  			res, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{DryRun: []string{mach.DryRunAll}})
   419  			if err != nil {
   420  				return err
   421  			}
   422  			*rsc = *res
   423  			return nil
   424  		}
   425  		return nil
   426  	})
   427  }
   428  
   429  func (c *Client) ApplyJob(rsc *batch.Job) error {
   430  	resources := c.set.BatchV1().Jobs(rsc.Namespace)
   431  	return c.applyResource("job", rsc.GetName(), func() error {
   432  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   433  		return err
   434  	}, func() error {
   435  		j, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   436  		if err != nil {
   437  			return err
   438  		}
   439  		if j.GetName() == rsc.GetName() && j.GetNamespace() == rsc.GetNamespace() {
   440  			_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   441  			return err
   442  		}
   443  		return nil
   444  	})
   445  }
   446  
   447  func (c *Client) WaitUntilJobCompleted(namespace string, name string, timeout time.Duration) error {
   448  	returnChannel := make(chan error, 1)
   449  	go func() {
   450  		ctx := context.Background()
   451  		job, err := c.set.BatchV1().Jobs(namespace).Get(ctx, name, mach.GetOptions{})
   452  		if err != nil {
   453  			returnChannel <- err
   454  			return
   455  		}
   456  
   457  		if job.Status.Succeeded > 0 {
   458  			returnChannel <- nil
   459  			return
   460  		}
   461  
   462  		labelSelector := getLabelSelector(job.Spec.Selector.MatchLabels)
   463  
   464  		watch, err := c.set.CoreV1().Pods(namespace).Watch(ctx, mach.ListOptions{
   465  			LabelSelector: labelSelector,
   466  		})
   467  		defer watch.Stop()
   468  		if err != nil {
   469  			returnChannel <- err
   470  			return
   471  		}
   472  
   473  		returnChannel <- waitForPodsPhase(watch, 1, core.PodSucceeded, false, false)
   474  	}()
   475  
   476  	select {
   477  	case res := <-returnChannel:
   478  		return res
   479  	case <-time.After(timeout):
   480  		return fmt.Errorf("timeout after %s while waiting for job to complete", timeout)
   481  	}
   482  }
   483  
   484  func (c *Client) DeleteJob(namespace string, name string) error {
   485  	job, err := c.GetJob(namespace, name)
   486  	if notFoundIsSuccess(err) != nil {
   487  		return err
   488  	}
   489  
   490  	if err := notFoundIsSuccess(c.set.BatchV1().Jobs(namespace).Delete(context.Background(), name, mach.DeleteOptions{})); err != nil {
   491  		return err
   492  	}
   493  
   494  	if job != nil && err == nil && job.Spec.Selector != nil && job.Spec.Selector.MatchLabels != nil {
   495  		//Pod cleanup if necessary
   496  		return notFoundIsSuccess(c.DeletePodsByLabels(namespace, job.Spec.Selector.MatchLabels))
   497  	}
   498  	return nil
   499  }
   500  
   501  func (c *Client) ListCronJobs(namespace string, labels map[string]string) (*batchv1beta1.CronJobList, error) {
   502  	return c.set.BatchV1beta1().CronJobs(namespace).List(context.Background(), mach.ListOptions{LabelSelector: getLabelSelector(labels)})
   503  }
   504  
   505  func (c *Client) ApplyCronJob(rsc *v1beta1.CronJob) error {
   506  	resources := c.set.BatchV1beta1().CronJobs(rsc.Namespace)
   507  	return c.applyResource("cronjob", rsc.GetName(), func() error {
   508  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   509  		return err
   510  	}, func() error {
   511  		j, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   512  		if err != nil {
   513  			return err
   514  		}
   515  		if j.GetName() == rsc.GetName() && j.GetNamespace() == rsc.GetNamespace() {
   516  			_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   517  			return err
   518  		}
   519  		return nil
   520  	})
   521  }
   522  
   523  func (c *Client) DeleteCronJob(namespace string, name string) error {
   524  	return notFoundIsSuccess(c.set.BatchV1beta1().CronJobs(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   525  }
   526  
   527  func (c *Client) ListPods(namespace string, labels map[string]string) (*core.PodList, error) {
   528  	return c.set.CoreV1().Pods(namespace).List(context.Background(), mach.ListOptions{LabelSelector: getLabelSelector(labels)})
   529  }
   530  
   531  func (c *Client) ApplyPodDisruptionBudget(rsc *policy.PodDisruptionBudget) error {
   532  	resources := c.set.PolicyV1beta1().PodDisruptionBudgets(rsc.Namespace)
   533  	return c.applyResource("poddisruptionbudget", rsc.GetName(), func() error {
   534  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   535  		return err
   536  	}, func() error {
   537  		pdb, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   538  		if err != nil {
   539  			return err
   540  		}
   541  		if pdb.GetName() == rsc.GetName() && pdb.GetNamespace() == rsc.GetNamespace() {
   542  			rsc.ResourceVersion = pdb.ResourceVersion
   543  			_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   544  			return err
   545  		}
   546  		return nil
   547  	})
   548  }
   549  func (c *Client) DeletePodDisruptionBudget(namespace string, name string) error {
   550  	return notFoundIsSuccess(c.set.PolicyV1beta1().PodDisruptionBudgets(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   551  }
   552  
   553  func (c *Client) ApplyStatefulSet(rsc *apps.StatefulSet, force bool) error {
   554  	resources := c.set.AppsV1().StatefulSets(rsc.Namespace)
   555  	rscLabels, err := labels.NameFrom(rsc.Labels)
   556  	if err != nil {
   557  		return err
   558  	}
   559  
   560  	rscSelector, err := labels.SelectorFrom(rsc.Spec.Selector.MatchLabels)
   561  	if err != nil {
   562  		return err
   563  	}
   564  
   565  	return c.applyController(
   566  		"statefulset",
   567  		force,
   568  		rscLabels,
   569  		rscSelector,
   570  		func() (*labels.Selector, error) {
   571  			sts, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   572  			if err != nil {
   573  				return nil, err
   574  			}
   575  
   576  			return labels.SelectorFrom(sts.Spec.Selector.MatchLabels)
   577  		},
   578  		func() error {
   579  			_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   580  			return err
   581  		},
   582  		func() error {
   583  			_, err = resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   584  			return err
   585  		},
   586  		func() error {
   587  			return c.DeleteStatefulset(rsc.GetNamespace(), rsc.GetName())
   588  		},
   589  	)
   590  }
   591  
   592  func (c *Client) DeleteStatefulset(namespace, name string) error {
   593  	return notFoundIsSuccess(c.set.AppsV1().StatefulSets(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   594  }
   595  
   596  func (c *Client) ScaleStatefulset(namespace, name string, replicaCount int) error {
   597  	patch := []byte(`{"spec":{"replicas":` + strconv.Itoa(replicaCount) + `}}`)
   598  	_, err := c.set.AppsV1().StatefulSets(namespace).Patch(context.Background(), name, types.StrategicMergePatchType, patch, mach.PatchOptions{})
   599  	return err
   600  }
   601  
   602  func (c *Client) WaitUntilStatefulsetIsReady(namespace string, name string, containerCheck, readyCheck bool, timeout time.Duration) error {
   603  	returnChannel := make(chan error, 1)
   604  	go func() {
   605  		ctx := context.Background()
   606  		sfs, err := c.set.AppsV1().StatefulSets(namespace).Get(ctx, name, mach.GetOptions{})
   607  		if err != nil {
   608  			returnChannel <- err
   609  			return
   610  		}
   611  
   612  		labelSelector := getLabelSelector(sfs.Spec.Selector.MatchLabels)
   613  
   614  		watch, err := c.set.CoreV1().Pods(namespace).Watch(ctx, mach.ListOptions{
   615  			LabelSelector: labelSelector,
   616  		})
   617  		defer watch.Stop()
   618  		if err != nil {
   619  			returnChannel <- err
   620  			return
   621  		}
   622  		replicas := sfs.Spec.Replicas
   623  
   624  		returnChannel <- waitForPodsPhase(watch, int(*replicas), core.PodRunning, containerCheck, readyCheck)
   625  	}()
   626  
   627  	select {
   628  	case res := <-returnChannel:
   629  		return res
   630  	case <-time.After(timeout):
   631  		return errors.New("timeout while waiting for job to complete")
   632  	}
   633  }
   634  
   635  func (c *Client) ApplySecret(rsc *core.Secret) error {
   636  	resources := c.set.CoreV1().Secrets(rsc.GetNamespace())
   637  	return c.applyResource("secret", rsc.GetName(), func() error {
   638  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   639  		return err
   640  	}, func() error {
   641  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   642  		return err
   643  	})
   644  }
   645  func (c *Client) GetSecret(namespace string, name string) (*core.Secret, error) {
   646  	return c.set.CoreV1().Secrets(namespace).Get(context.Background(), name, mach.GetOptions{})
   647  }
   648  
   649  func (c *Client) WaitForSecret(namespace string, name string, timeout time.Duration) error {
   650  	ctx := context.Background()
   651  	return await(
   652  		timeout,
   653  		func() (interface{}, error) {
   654  			return c.set.CoreV1().Secrets(namespace).Get(ctx, name, mach.GetOptions{})
   655  		},
   656  	)
   657  }
   658  
   659  func await(timeout time.Duration, getResource func() (interface{}, error)) error {
   660  	ticker := time.NewTicker(time.Second)
   661  	defer ticker.Stop()
   662  	timer := time.NewTimer(timeout)
   663  	defer timer.Stop()
   664  	for {
   665  		select {
   666  		case <-ticker.C:
   667  			resource, err := getResource()
   668  			if err != nil && !macherrs.IsNotFound(err) {
   669  				return err
   670  			} else if resource != nil {
   671  				return nil
   672  			}
   673  
   674  		case <-timer.C:
   675  			return errors.New("timeout while waiting for secret to be created")
   676  		}
   677  	}
   678  }
   679  
   680  func (c *Client) DeleteSecret(namespace, name string) error {
   681  	return notFoundIsSuccess(c.set.CoreV1().Secrets(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   682  }
   683  
   684  func (c *Client) GetConfigMap(namespace, name string) (*core.ConfigMap, error) {
   685  	return c.set.CoreV1().ConfigMaps(namespace).Get(context.Background(), name, mach.GetOptions{})
   686  }
   687  
   688  func (c *Client) ApplyConfigmap(rsc *core.ConfigMap) error {
   689  	resources := c.set.CoreV1().ConfigMaps(rsc.GetNamespace())
   690  	return c.applyResource("secret", rsc.GetName(), func() error {
   691  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   692  		return err
   693  	}, func() error {
   694  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   695  		return err
   696  	})
   697  }
   698  
   699  func (c *Client) DeleteConfigmap(namespace, name string) error {
   700  	return notFoundIsSuccess(c.set.CoreV1().ConfigMaps(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   701  }
   702  
   703  func (c *Client) ApplyServiceAccount(rsc *core.ServiceAccount) error {
   704  	resources := c.set.CoreV1().ServiceAccounts(rsc.Namespace)
   705  	return c.applyResource("serviceaccount", rsc.GetName(), func() error {
   706  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   707  		return err
   708  	}, func() error {
   709  		sa, err := resources.Get(context.Background(), rsc.GetName(), mach.GetOptions{})
   710  		if err != nil {
   711  			return err
   712  		}
   713  
   714  		different := false
   715  		//workaround as 1 token will always be created by kubeapi
   716  		if (!(sa.Secrets != nil && len(sa.Secrets) == 1 && (rsc.Secrets == nil || len(rsc.Secrets) == 0)) && (!reflect.DeepEqual(sa.Secrets, rsc.Secrets))) ||
   717  			(sa.ImagePullSecrets != nil && rsc.ImagePullSecrets != nil && !reflect.DeepEqual(sa.ImagePullSecrets, rsc.ImagePullSecrets)) ||
   718  			(sa.AutomountServiceAccountToken != nil && rsc.AutomountServiceAccountToken != nil && *sa.AutomountServiceAccountToken != *rsc.AutomountServiceAccountToken) {
   719  			different = true
   720  		}
   721  
   722  		if different &&
   723  			sa.GetName() == rsc.GetName() &&
   724  			sa.GetNamespace() == rsc.GetNamespace() {
   725  
   726  			_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   727  			return err
   728  		}
   729  		return nil
   730  	})
   731  }
   732  
   733  func (c *Client) WaitForConfigMap(namespace string, name string, timeout time.Duration) error {
   734  	ctx := context.Background()
   735  	return await(
   736  		timeout,
   737  		func() (interface{}, error) {
   738  			return c.set.CoreV1().ConfigMaps(namespace).Get(ctx, name, mach.GetOptions{})
   739  		},
   740  	)
   741  }
   742  
   743  func (c *Client) DeleteServiceAccount(namespace, name string) error {
   744  	return notFoundIsSuccess(c.set.CoreV1().ServiceAccounts(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   745  }
   746  
   747  func (c *Client) ApplyIngress(rsc *extensions.Ingress) error {
   748  	resources := c.set.ExtensionsV1beta1().Ingresses(rsc.GetNamespace())
   749  	return c.applyResource("ingress", rsc.GetName(), func() error {
   750  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   751  		return err
   752  	}, func() error {
   753  		svc, err := resources.Get(context.Background(), rsc.Name, mach.GetOptions{})
   754  		if err != nil {
   755  			return err
   756  		}
   757  		rsc.ObjectMeta.ResourceVersion = svc.ObjectMeta.ResourceVersion
   758  		_, err = resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   759  		return err
   760  	})
   761  }
   762  
   763  func (c *Client) DeleteIngress(namespace, name string) error {
   764  	return notFoundIsSuccess(c.set.ExtensionsV1beta1().Ingresses(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   765  }
   766  
   767  func (c *Client) ApplyRole(rsc *rbac.Role) error {
   768  	resources := c.set.RbacV1().Roles(rsc.Namespace)
   769  	return c.applyResource("role", rsc.GetName(), func() error {
   770  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   771  		return err
   772  	}, func() error {
   773  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   774  		return err
   775  	})
   776  }
   777  func (c *Client) DeleteRole(namespace, name string) error {
   778  	return notFoundIsSuccess(c.set.RbacV1().Roles(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   779  }
   780  
   781  func (c *Client) ApplyClusterRole(rsc *rbac.ClusterRole) error {
   782  	resources := c.set.RbacV1().ClusterRoles()
   783  	return c.applyResource("clusterrole", rsc.GetName(), func() error {
   784  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   785  		return err
   786  	}, func() error {
   787  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   788  		return err
   789  	})
   790  }
   791  
   792  func (c *Client) DeleteClusterRole(name string) error {
   793  	return notFoundIsSuccess(c.set.RbacV1().ClusterRoles().Delete(context.Background(), name, mach.DeleteOptions{}))
   794  }
   795  
   796  func (c *Client) ApplyRoleBinding(rsc *rbac.RoleBinding) error {
   797  	resources := c.set.RbacV1().RoleBindings(rsc.Namespace)
   798  	return c.applyResource("rolebinding", rsc.GetName(), func() error {
   799  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   800  		return err
   801  	}, func() error {
   802  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   803  		return err
   804  	})
   805  }
   806  
   807  func (c *Client) DeleteRoleBinding(namespace, name string) error {
   808  	return notFoundIsSuccess(c.set.RbacV1().RoleBindings(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
   809  }
   810  
   811  func (c *Client) ApplyClusterRoleBinding(rsc *rbac.ClusterRoleBinding) error {
   812  	resources := c.set.RbacV1().ClusterRoleBindings()
   813  	return c.applyResource("clusterrolebinding", rsc.GetName(), func() error {
   814  		_, err := resources.Create(context.Background(), rsc, mach.CreateOptions{})
   815  		return err
   816  	}, func() error {
   817  		_, err := resources.Update(context.Background(), rsc, mach.UpdateOptions{})
   818  		return err
   819  	})
   820  }
   821  
   822  func (c *Client) DeleteClusterRoleBinding(name string) error {
   823  	return notFoundIsSuccess(c.set.RbacV1().ClusterRoleBindings().Delete(context.Background(), name, mach.DeleteOptions{}))
   824  }
   825  
   826  type recreateErr struct{}
   827  
   828  func (r *recreateErr) Error() string { return "recreate" }
   829  
   830  func (c *Client) applyResource(object, name string, create func() error, update func() error) (err error) {
   831  
   832  	defer func() {
   833  		if err != nil {
   834  			err = fmt.Errorf("applying %s %s failed: %w", object, name, err)
   835  		}
   836  	}()
   837  
   838  	err = update()
   839  	_, recreate := err.(*recreateErr)
   840  	if err == nil || (!macherrs.IsNotFound(err) && !recreate) {
   841  		return err
   842  	}
   843  	return create()
   844  }
   845  
   846  func (c *Client) applyController(
   847  	controllerType string,
   848  	force bool,
   849  	newNameLabels *labels.Name,
   850  	newSelector *labels.Selector,
   851  	getCurrentSelectorFunc func() (*labels.Selector, error),
   852  	createResource func() error,
   853  	updateResource func() error,
   854  	deleteResource func() error) error {
   855  
   856  	return c.applyResource(
   857  		controllerType,
   858  		newNameLabels.Name(),
   859  		createResource,
   860  		func() error {
   861  
   862  			currentSelector, err := getCurrentSelectorFunc()
   863  			if err != nil {
   864  				return err
   865  			}
   866  
   867  			if newSelector.Equal(currentSelector) {
   868  				return updateResource()
   869  			}
   870  
   871  			if !force {
   872  				return fmt.Errorf("only recreating %s when force is true", controllerType)
   873  			}
   874  
   875  			if err := deleteResource(); err != nil {
   876  				return err
   877  			}
   878  			return &recreateErr{}
   879  		},
   880  	)
   881  }
   882  
   883  func restCfgFromContent(bytes []byte) (cfg *rest.Config, err error) {
   884  	defer func() {
   885  		if err != nil {
   886  			err = fmt.Errorf("creating a kubernetes client from bytes failed: %w", err)
   887  		}
   888  	}()
   889  
   890  	clientCfg, err := clientcmd.NewClientConfigFromBytes(bytes)
   891  	if err != nil {
   892  		return nil, err
   893  	}
   894  
   895  	return clientCfg.ClientConfig()
   896  }
   897  
   898  func (c *Client) init(kubeconfig *string, kubeconfigPath string) (err error) {
   899  	defer func() {
   900  		if err != nil {
   901  			err = fmt.Errorf("refreshing Kubernetes client failed: %w", err)
   902  		}
   903  	}()
   904  
   905  	restCfg := new(rest.Config)
   906  	if kubeconfig != nil && *kubeconfig != "" {
   907  		c.monitor.WithField("content", *kubeconfig).Debug("trying kubeconfig from in-memory content")
   908  		restCfg, err = restCfgFromContent([]byte(*kubeconfig))
   909  		if err != nil {
   910  			return err
   911  		}
   912  	} else {
   913  
   914  		var inClusterErr error
   915  		restCfg, inClusterErr = rest.InClusterConfig()
   916  		if inClusterErr == nil {
   917  			return c.refreshAllClients(restCfg)
   918  		}
   919  
   920  		localKubeconfigContent, localKubeconfigErr := ioutil.ReadFile(kubeconfigPath)
   921  		if localKubeconfigErr != nil {
   922  			return fmt.Errorf("can't create kubernetes client: in-cluster err: %w: local kubeconfig err: %s", inClusterErr, localKubeconfigErr)
   923  		}
   924  
   925  		restCfg, localKubeconfigErr = restCfgFromContent(localKubeconfigContent)
   926  		if localKubeconfigErr != nil {
   927  			return fmt.Errorf("can't create kubernetes client: in-cluster err: %w: local kubeconfig err: %s", inClusterErr, localKubeconfigErr)
   928  		}
   929  	}
   930  
   931  	return c.refreshAllClients(restCfg)
   932  }
   933  
   934  func (c *Client) initConfig(config *rest.Config) (err error) {
   935  	return c.refreshAllClients(config)
   936  }
   937  
   938  func (c *Client) refreshAllClients(config *rest.Config) (err error) {
   939  	defer func() {
   940  		if err != nil {
   941  			err = fmt.Errorf("refreshing all kubernetes clients failed: %w", err)
   942  		}
   943  	}()
   944  
   945  	c.restConfig = config
   946  
   947  	set, err := kubernetes.NewForConfig(config)
   948  	if err != nil {
   949  		return fmt.Errorf("creating Clientset failed: %w", err)
   950  	}
   951  	c.set = set
   952  
   953  	apixv1beta1clientC, err := apixv1beta1client.NewForConfig(config)
   954  	if err != nil {
   955  		return fmt.Errorf("creating ApiextensionsV1beta1Client failed: %w", err)
   956  	}
   957  	c.apixv1beta1client = apixv1beta1clientC
   958  
   959  	dynamicC, err := dynamic.NewForConfig(config)
   960  	if err != nil {
   961  		return fmt.Errorf("creating dynamic client failed: %w", err)
   962  	}
   963  	c.dynamic = dynamicC
   964  
   965  	dc, err := discovery.NewDiscoveryClientForConfig(config)
   966  	if err != nil {
   967  		return fmt.Errorf("creating DiscoveryClient failed: %w", err)
   968  	}
   969  	c.mapper = restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc))
   970  
   971  	return c.checkConnectivity()
   972  }
   973  
   974  func (c *Client) GetNode(id string) (node *core.Node, err error) {
   975  	return c.nodeApi().Get(context.Background(), id, mach.GetOptions{})
   976  }
   977  
   978  func (c *Client) ListNodes(filterID ...string) (nodes []core.Node, err error) {
   979  	defer func() {
   980  		if err != nil {
   981  			err = fmt.Errorf("listing nodes %s failed: %w", strings.Join(filterID, ", "), err)
   982  		}
   983  	}()
   984  
   985  	labelSelector := ""
   986  	for _, id := range filterID {
   987  		labelSelector = fmt.Sprintf("%s,name=%s", labelSelector, id)
   988  	}
   989  	if len(labelSelector) > 0 {
   990  		labelSelector = labelSelector[1:]
   991  	}
   992  
   993  	nodeList, err := c.nodeApi().List(context.Background(), mach.ListOptions{
   994  		LabelSelector: labelSelector,
   995  	})
   996  
   997  	if err != nil {
   998  		return nil, err
   999  	}
  1000  	return nodeList.Items, nil
  1001  }
  1002  
  1003  func (c *Client) UpdateNode(node *core.Node) (err error) {
  1004  
  1005  	defer func() {
  1006  		if err != nil {
  1007  			err = fmt.Errorf("updating node %s failed: %w", node.GetName(), err)
  1008  		}
  1009  	}()
  1010  
  1011  	node.ResourceVersion = ""
  1012  	_, err = c.nodeApi().Update(context.Background(), node, mach.UpdateOptions{})
  1013  	return err
  1014  }
  1015  
  1016  type DrainReason int
  1017  
  1018  const (
  1019  	Updating DrainReason = iota
  1020  	Rebooting
  1021  	Deleting
  1022  )
  1023  
  1024  func (c *Client) cordon(node *core.Node, reason DrainReason) (err error) {
  1025  	defer func() {
  1026  		if err != nil {
  1027  			err = fmt.Errorf("cordoning node %s failed: %w", node.GetName(), err)
  1028  		}
  1029  	}()
  1030  
  1031  	monitor := c.monitor.WithFields(map[string]interface{}{
  1032  		"machine": node.GetName(),
  1033  	})
  1034  	monitor.Info("Cordoning node")
  1035  
  1036  	if c.Tainted(node, reason) {
  1037  		return nil
  1038  	}
  1039  	node.Spec.Taints = append(node.Spec.Taints, core.Taint{
  1040  		Key:    TaintKeyPrefix + reason.String(),
  1041  		Effect: "NoSchedule",
  1042  	})
  1043  
  1044  	if err := c.UpdateNode(node); err != nil {
  1045  		return err
  1046  	}
  1047  	monitor.Info("Node cordoned")
  1048  	return nil
  1049  }
  1050  
  1051  type Machine interface {
  1052  	GetUpdating() bool
  1053  	SetUpdating(bool)
  1054  	GetJoined() bool
  1055  	SetJoined(bool)
  1056  }
  1057  
  1058  func (c *Client) Tainted(node *core.Node, reason DrainReason) bool {
  1059  	return c.tainted(node.Spec.Taints, reason) != -1
  1060  }
  1061  
  1062  func (c *Client) tainted(taints []core.Taint, reason DrainReason) int {
  1063  
  1064  	for idx, taint := range taints {
  1065  		if taint.Key == TaintKeyPrefix+reason.String() {
  1066  			return idx
  1067  		}
  1068  	}
  1069  	return -1
  1070  }
  1071  
  1072  func (c *Client) RemoveFromTaints(taints []core.Taint, reason DrainReason) (result []core.Taint) {
  1073  	idx := c.tainted(taints, reason)
  1074  	if idx < 0 {
  1075  		return taints
  1076  	}
  1077  	return append(taints[0:idx], taints[idx+1:]...)
  1078  }
  1079  
  1080  func (c *Client) Drain(machine Machine, node *core.Node, reason DrainReason, self bool) (err error) {
  1081  	defer func() {
  1082  		if err != nil {
  1083  			err = fmt.Errorf("draining node %s failed: %w", node.GetName(), err)
  1084  		}
  1085  	}()
  1086  
  1087  	monitor := c.monitor.WithFields(map[string]interface{}{
  1088  		"machine": node.GetName(),
  1089  	})
  1090  	monitor.Info("Draining node")
  1091  
  1092  	if err = c.cordon(node, reason); err != nil {
  1093  		return err
  1094  	}
  1095  
  1096  	if err := c.evictPods(node, self); err != nil {
  1097  		return err
  1098  	}
  1099  	if !machine.GetUpdating() {
  1100  		machine.SetUpdating(true)
  1101  		monitor.Changed("Node drained")
  1102  	}
  1103  	return nil
  1104  }
  1105  
  1106  func (c *Client) DeleteNode(name string) error {
  1107  	return notFoundIsSuccess(c.set.CoreV1().Nodes().Delete(context.Background(), name, mach.DeleteOptions{}))
  1108  }
  1109  
  1110  func (c *Client) evictPods(node *core.Node, self bool) (err error) {
  1111  
  1112  	defer func() {
  1113  		if err != nil {
  1114  			err = fmt.Errorf("evicting pods from node %s failed: %w", node.GetName(), err)
  1115  		}
  1116  	}()
  1117  
  1118  	monitor := c.monitor.WithFields(map[string]interface{}{
  1119  		"machine": node.GetName(),
  1120  	})
  1121  
  1122  	monitor.Info("Evicting pods")
  1123  
  1124  	fieldSelector := fmt.Sprintf("spec.nodeName=%s,status.phase=Running", node.Name) // possibly selects self too
  1125  	var labelSelector string
  1126  	if !self {
  1127  		labelSelector = "app.kubernetes.io/name!=orbiter" // exclude self
  1128  	}
  1129  	podItems, err := c.set.CoreV1().Pods("").List(context.Background(), mach.ListOptions{
  1130  		FieldSelector: fieldSelector,
  1131  		LabelSelector: labelSelector,
  1132  	})
  1133  	if err != nil {
  1134  		return fmt.Errorf("listing pods with field-selector %s and label-selector %s failed: %w", fieldSelector, labelSelector, err)
  1135  	}
  1136  
  1137  	// --ignore-daemonsets
  1138  	var pods []core.Pod
  1139  	for i := range podItems.Items {
  1140  		pod := podItems.Items[i]
  1141  		controllerRef := mach.GetControllerOf(&pod)
  1142  		if controllerRef == nil || controllerRef.Kind != apps.SchemeGroupVersion.WithKind("DaemonSet").Kind {
  1143  			pods = append(pods, pod)
  1144  		}
  1145  	}
  1146  
  1147  	var wg sync.WaitGroup
  1148  	synchronizer := helpers.NewSynchronizer(&wg)
  1149  
  1150  	for _, p := range pods {
  1151  		wg.Add(1)
  1152  		go func(pod core.Pod) {
  1153  
  1154  			var gracePeriodSeconds int64 = 60
  1155  			monitor := c.monitor.WithFields(map[string]interface{}{
  1156  				"pod":       pod.GetName(),
  1157  				"namespace": pod.GetNamespace(),
  1158  			})
  1159  			monitor.Debug("Evicting pod")
  1160  
  1161  			watcher, goErr := c.set.CoreV1().Pods(pod.Namespace).Watch(context.Background(), mach.ListOptions{
  1162  				FieldSelector: fmt.Sprintf("metadata.name=%s", pod.Name),
  1163  			})
  1164  			if goErr != nil {
  1165  				synchronizer.Done(nil)
  1166  				return
  1167  			}
  1168  			defer watcher.Stop()
  1169  
  1170  			if goErr := c.set.PolicyV1beta1().Evictions(pod.Namespace).Evict(context.Background(), &policy.Eviction{
  1171  				TypeMeta: mach.TypeMeta{
  1172  					Kind:       "EvictionKind",
  1173  					APIVersion: c.set.PolicyV1beta1().RESTClient().APIVersion().String(),
  1174  				},
  1175  				ObjectMeta: mach.ObjectMeta{
  1176  					Name:      pod.Name,
  1177  					Namespace: pod.Namespace,
  1178  				},
  1179  				DeleteOptions: &mach.DeleteOptions{
  1180  					GracePeriodSeconds: &gracePeriodSeconds,
  1181  				},
  1182  			}); goErr != nil {
  1183  				synchronizer.Done(fmt.Errorf("evicting pod %s failed: %w", pod.Name, goErr))
  1184  				return
  1185  			}
  1186  			monitor.Debug("Watching pod")
  1187  
  1188  			timeout := time.After(time.Duration(safeUint64(pod.Spec.TerminationGracePeriodSeconds)) + 30)
  1189  			for {
  1190  				select {
  1191  				case event := <-watcher.ResultChan():
  1192  					wPod, ok := event.Object.(*core.Pod)
  1193  					if !ok {
  1194  						continue
  1195  					}
  1196  					monitor = monitor.WithFields(map[string]interface{}{
  1197  						"event": event.Type,
  1198  					})
  1199  					monitor.Debug("Pod event happened")
  1200  					if event.Type == watch.Deleted {
  1201  						monitor.WithFields(map[string]interface{}{
  1202  							"new_node": wPod.Spec.NodeName,
  1203  						}).Debug("Pod evicted")
  1204  						synchronizer.Done(nil)
  1205  						return
  1206  					}
  1207  				case <-timeout:
  1208  
  1209  					delErr := c.set.CoreV1().Pods(pod.Namespace).Delete(context.Background(), pod.Name, mach.DeleteOptions{})
  1210  					if delErr != nil {
  1211  						delErr = fmt.Errorf("deleting pod %s after timout exceeded failed: %w", pod.Name, delErr)
  1212  					}
  1213  					synchronizer.Done(notFoundIsSuccess(delErr))
  1214  					return
  1215  				}
  1216  			}
  1217  		}(p)
  1218  	}
  1219  	wg.Wait()
  1220  
  1221  	if synchronizer.IsError() {
  1222  		return fmt.Errorf("concurrently evicting pods from node %s failed: %w", node.Name, synchronizer)
  1223  	}
  1224  
  1225  	monitor.Info("Pods evicted")
  1226  	return nil
  1227  }
  1228  
  1229  func safeUint64(ptr *int64) int64 {
  1230  	if ptr == nil {
  1231  		return 0
  1232  	}
  1233  	return *ptr
  1234  }
  1235  func (c *Client) DeletePodsByLabels(namespace string, labels map[string]string) error {
  1236  	return notFoundIsSuccess(c.set.CoreV1().Pods(namespace).DeleteCollection(context.Background(), mach.DeleteOptions{}, mach.ListOptions{
  1237  		LabelSelector: getLabelSelector(labels),
  1238  	}))
  1239  }
  1240  
  1241  func (c *Client) DeletePod(namespace, name string) error {
  1242  	return notFoundIsSuccess(c.set.CoreV1().Pods(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
  1243  }
  1244  
  1245  func getLabelSelector(labels map[string]string) string {
  1246  	labelSelector := ""
  1247  	for k, v := range labels {
  1248  		if labelSelector == "" {
  1249  			labelSelector = k + "=" + v
  1250  		} else {
  1251  			labelSelector = labelSelector + ", " + k + "=" + v
  1252  
  1253  		}
  1254  	}
  1255  	return labelSelector
  1256  }
  1257  
  1258  type podStatus struct {
  1259  	name              string
  1260  	phase             core.PodPhase
  1261  	containerStatuses []containerStatus
  1262  }
  1263  type containerStatus struct {
  1264  	containerState core.ContainerStatus
  1265  }
  1266  
  1267  func waitForPodsPhase(watch watch.Interface, replicaCount int, phase core.PodPhase, containerCheck bool, readyCheck bool) error {
  1268  	statusChan := make(chan podStatus)
  1269  	pods := make(map[string]core.PodPhase, 0)
  1270  
  1271  	if watch == nil {
  1272  		return errors.New("no pods watched")
  1273  	}
  1274  
  1275  	go func() {
  1276  		for event := range watch.ResultChan() {
  1277  			p, ok := event.Object.(*core.Pod)
  1278  			if !ok {
  1279  				continue
  1280  			}
  1281  			containerStatuses := make([]containerStatus, 0)
  1282  			for _, podContainerStatus := range p.Status.ContainerStatuses {
  1283  				containerStatuses = append(containerStatuses, containerStatus{
  1284  					containerState: podContainerStatus,
  1285  				})
  1286  			}
  1287  
  1288  			statusChan <- podStatus{p.Name, p.Status.Phase, containerStatuses}
  1289  
  1290  		}
  1291  	}()
  1292  
  1293  	runningPods := 0
  1294  	for runningPods < replicaCount {
  1295  		podStat := <-statusChan
  1296  		if podStat.phase == phase {
  1297  			running := true
  1298  			ready := true
  1299  			if containerCheck {
  1300  				running = checkContainer(podStat.containerStatuses)
  1301  			}
  1302  			if readyCheck {
  1303  				ready = checkReady(podStat.containerStatuses)
  1304  			}
  1305  
  1306  			if running && ready {
  1307  				pods[podStat.name] = podStat.phase
  1308  			} else {
  1309  				delete(pods, podStat.name)
  1310  			}
  1311  		} else {
  1312  			delete(pods, podStat.name)
  1313  		}
  1314  		runningPods = len(pods)
  1315  	}
  1316  	return nil
  1317  }
  1318  
  1319  func checkContainer(containers []containerStatus) bool {
  1320  	running := true
  1321  	for _, stat := range containers {
  1322  		if stat.containerState.State.Running == nil {
  1323  			running = false
  1324  		}
  1325  	}
  1326  	return running
  1327  }
  1328  
  1329  func checkReady(containers []containerStatus) bool {
  1330  	ready := true
  1331  	for _, stat := range containers {
  1332  		if !stat.containerState.Ready {
  1333  			ready = false
  1334  		}
  1335  	}
  1336  	return ready
  1337  }
  1338  
  1339  func (c *Client) CheckCRD(name string) (*apixv1beta1.CustomResourceDefinition, bool, error) {
  1340  	crds := c.apixv1beta1client.CustomResourceDefinitions()
  1341  	crd, err := crds.Get(context.Background(), name, mach.GetOptions{})
  1342  	if macherrs.IsNotFound(err) {
  1343  		return nil, false, nil
  1344  	}
  1345  	return crd, true, err
  1346  }
  1347  
  1348  func (c *Client) GetNamespacedCRDResource(group, version, kind, namespace, name string) (*unstructured.Unstructured, error) {
  1349  	mapping, err := c.mapper.RESTMapping(schema.GroupKind{
  1350  		Group: group,
  1351  		Kind:  kind,
  1352  	}, version)
  1353  	if err != nil {
  1354  		return nil, err
  1355  	}
  1356  	resource := c.dynamic.Resource(mapping.Resource).Namespace(namespace)
  1357  
  1358  	return resource.Get(context.Background(), name, mach.GetOptions{})
  1359  }
  1360  
  1361  func (c *Client) ApplyNamespacedCRDResource(group, version, kind, namespace, name string, crd *unstructured.Unstructured) error {
  1362  	mapping, err := c.mapper.RESTMapping(schema.GroupKind{
  1363  		Group: group,
  1364  		Kind:  kind,
  1365  	}, version)
  1366  	if err != nil {
  1367  		return err
  1368  	}
  1369  
  1370  	resources := c.dynamic.Resource(mapping.Resource).Namespace(namespace)
  1371  	existing, err := resources.Get(context.Background(), name, mach.GetOptions{})
  1372  	if err != nil && !macherrs.IsNotFound(err) {
  1373  		return fmt.Errorf("getting existing crd %s of kind %s failed: %w", name, kind, err)
  1374  	}
  1375  	update := func() error {
  1376  		return err
  1377  	}
  1378  	if err == nil {
  1379  		crd.SetResourceVersion(existing.GetResourceVersion())
  1380  		update = func() error {
  1381  			_, err := resources.Update(context.Background(), crd, mach.UpdateOptions{})
  1382  			return err
  1383  		}
  1384  	}
  1385  
  1386  	return c.applyResource("crd", name, func() error {
  1387  		_, err := resources.Create(context.Background(), crd, mach.CreateOptions{})
  1388  		return err
  1389  	}, update)
  1390  }
  1391  
  1392  func (c *Client) DeleteNamespacedCRDResource(group, version, kind, namespace, name string) error {
  1393  	mapping, err := c.mapper.RESTMapping(schema.GroupKind{
  1394  		Group: group,
  1395  		Kind:  kind,
  1396  	}, version)
  1397  	if err != nil {
  1398  		if _, ok := err.(*meta.NoKindMatchError); ok {
  1399  			return nil
  1400  		}
  1401  		return err
  1402  	}
  1403  
  1404  	return notFoundIsSuccess(c.dynamic.Resource(mapping.Resource).Namespace(namespace).Delete(context.Background(), name, mach.DeleteOptions{}))
  1405  }
  1406  
  1407  func (c *Client) ApplyCRDResource(crd *unstructured.Unstructured) error {
  1408  	kind := crd.Object["kind"].(string)
  1409  	apiVersion := strings.Split(crd.Object["apiVersion"].(string), "/")
  1410  	metadata := crd.Object["metadata"].(map[string]interface{})
  1411  	name := metadata["name"].(string)
  1412  
  1413  	apiVersionGroup := ""
  1414  	apiVersionVersion := ""
  1415  	switch len(apiVersion) {
  1416  	case 2:
  1417  		apiVersionGroup = apiVersion[0]
  1418  		apiVersionVersion = apiVersion[1]
  1419  	case 1:
  1420  		apiVersionVersion = apiVersion[0]
  1421  	default:
  1422  		return fmt.Errorf("")
  1423  	}
  1424  
  1425  	mapping, err := c.mapper.RESTMapping(schema.GroupKind{
  1426  		Group: apiVersionGroup,
  1427  		Kind:  kind,
  1428  	}, apiVersionVersion)
  1429  	if err != nil {
  1430  		return err
  1431  	}
  1432  
  1433  	var resources dynamic.ResourceInterface
  1434  	clusterResources := c.dynamic.Resource(mapping.Resource)
  1435  	resources = clusterResources
  1436  	namespace, ok := metadata["namespace"]
  1437  	if ok && namespace.(string) != "" {
  1438  		resources = clusterResources.Namespace(namespace.(string))
  1439  	}
  1440  
  1441  	existing, err := resources.Get(context.Background(), name, mach.GetOptions{})
  1442  	if err != nil && !macherrs.IsNotFound(err) {
  1443  		return fmt.Errorf("getting existing crd %s of kind %s failed: %w", name, kind, err)
  1444  	}
  1445  	if err == nil {
  1446  		crd.SetResourceVersion(existing.GetResourceVersion())
  1447  	}
  1448  	err = nil
  1449  
  1450  	return c.applyResource("crd", name, func() error {
  1451  		_, err := resources.Create(context.Background(), crd, mach.CreateOptions{})
  1452  		return err
  1453  	}, func() error {
  1454  		_, err := resources.Update(context.Background(), crd, mach.UpdateOptions{})
  1455  		return err
  1456  	})
  1457  }
  1458  
  1459  func (c *Client) DeleteCRDResource(group, version, kind, name string) error {
  1460  	mapping, err := c.mapper.RESTMapping(schema.GroupKind{
  1461  		Group: group,
  1462  		Kind:  kind,
  1463  	}, version)
  1464  	if err != nil {
  1465  		return err
  1466  	}
  1467  
  1468  	return notFoundIsSuccess(c.dynamic.Resource(mapping.Resource).Delete(context.Background(), name, mach.DeleteOptions{}))
  1469  }
  1470  
  1471  func (c *Client) ExecInPodOfDeployment(namespace, name, container, command string) error {
  1472  	cmd := []string{
  1473  		"sh",
  1474  		"-c",
  1475  		command,
  1476  	}
  1477  	deploy, err := c.GetDeployment(namespace, name)
  1478  	if err != nil {
  1479  		return err
  1480  	}
  1481  
  1482  	labelSelector := ""
  1483  	for k, v := range deploy.Spec.Selector.MatchLabels {
  1484  		if labelSelector == "" {
  1485  			labelSelector = fmt.Sprintf("%s=%s", k, v)
  1486  		} else {
  1487  			labelSelector = fmt.Sprintf("%s, %s=%s", labelSelector, k, v)
  1488  		}
  1489  	}
  1490  	list, err := c.set.CoreV1().Pods(namespace).List(context.Background(), mach.ListOptions{
  1491  		LabelSelector: labelSelector,
  1492  	})
  1493  	if err != nil {
  1494  		return err
  1495  	}
  1496  
  1497  	firstPod := list.Items[0]
  1498  	req := c.set.CoreV1().RESTClient().Post().Resource("pods").Namespace(namespace).Name(firstPod.Name).SubResource("exec")
  1499  	return c.execInPod(cmd, container, req)
  1500  }
  1501  func (c *Client) ExecInPod(namespace, name, container, command string) error {
  1502  	cmd := []string{
  1503  		"sh",
  1504  		"-c",
  1505  		command,
  1506  	}
  1507  
  1508  	req := c.set.CoreV1().RESTClient().Post().Resource("pods").Namespace(namespace).Name(name).SubResource("exec")
  1509  	return c.execInPod(cmd, container, req)
  1510  }
  1511  
  1512  func (c *Client) ExecInPodWithOutput(namespace, name, container, command string) (string, error) {
  1513  	cmd := []string{
  1514  		"sh",
  1515  		"-c",
  1516  		command,
  1517  	}
  1518  
  1519  	req := c.set.CoreV1().RESTClient().Post().Resource("pods").Namespace(namespace).Name(name).SubResource("exec")
  1520  	return c.execInPodWithOutput(cmd, container, req)
  1521  }
  1522  
  1523  func (c *Client) execInPod(cmd []string, container string, req *rest.Request) error {
  1524  	option := &core.PodExecOptions{
  1525  		Command:   cmd,
  1526  		Stdin:     false,
  1527  		Stdout:    true,
  1528  		Stderr:    true,
  1529  		TTY:       false,
  1530  		Container: container,
  1531  	}
  1532  	req.VersionedParams(
  1533  		option,
  1534  		scheme.ParameterCodec,
  1535  	)
  1536  
  1537  	exec, err := remotecommand.NewSPDYExecutor(c.restConfig, "POST", req.URL())
  1538  	if err != nil {
  1539  		return err
  1540  	}
  1541  
  1542  	var stdout, stderr bytes.Buffer
  1543  	err = exec.Stream(remotecommand.StreamOptions{
  1544  		Stdin:  nil,
  1545  		Stdout: &stdout,
  1546  		Stderr: &stderr,
  1547  		Tty:    true,
  1548  	})
  1549  	if err != nil {
  1550  		return err
  1551  	}
  1552  
  1553  	errStr := stderr.Bytes()
  1554  	if len(errStr) > 0 {
  1555  		return errors.New(string(errStr))
  1556  	}
  1557  
  1558  	return nil
  1559  }
  1560  
  1561  func (c *Client) execInPodWithOutput(cmd []string, container string, req *rest.Request) (string, error) {
  1562  	option := &core.PodExecOptions{
  1563  		Command:   cmd,
  1564  		Stdin:     false,
  1565  		Stdout:    true,
  1566  		Stderr:    true,
  1567  		TTY:       false,
  1568  		Container: container,
  1569  	}
  1570  	req.VersionedParams(
  1571  		option,
  1572  		scheme.ParameterCodec,
  1573  	)
  1574  
  1575  	exec, err := remotecommand.NewSPDYExecutor(c.restConfig, "POST", req.URL())
  1576  	if err != nil {
  1577  		return "", err
  1578  	}
  1579  
  1580  	var stdout, stderr bytes.Buffer
  1581  	err = exec.Stream(remotecommand.StreamOptions{
  1582  		Stdin:  nil,
  1583  		Stdout: &stdout,
  1584  		Stderr: &stderr,
  1585  		Tty:    true,
  1586  	})
  1587  	if err != nil {
  1588  		return "", err
  1589  	}
  1590  
  1591  	errStr := stderr.Bytes()
  1592  	if len(errStr) > 0 {
  1593  		return "", errors.New(string(errStr))
  1594  	}
  1595  
  1596  	outData := stdout.Bytes()
  1597  	return string(outData), nil
  1598  }
  1599  
  1600  func notFoundIsSuccess(err error) error {
  1601  	if macherrs.IsNotFound(err) {
  1602  		return nil
  1603  	}
  1604  	return err
  1605  }
  1606  
  1607  func (c *Client) ApplyPlainYAML(monitor mntr.Monitor, data []byte) error {
  1608  	utilyaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(data)))
  1609  
  1610  	return forEachObjectInYAML(monitor, data, c.ApplyCRDResource)
  1611  }
  1612  
  1613  // ForEachObjectInYAMLActionFunc is a function that is executed against each
  1614  // object found in a YAML document.
  1615  // When a non-empty namespace is provided then the object is assigned the
  1616  // namespace prior to any other actions being performed with or to the object.
  1617  // this is heavily ispired by https://github.com/kubernetes/client-go/issues/216#issuecomment-718813670
  1618  type forEachObjectInYAMLActionFunc func(*unstructured.Unstructured) error
  1619  
  1620  // ForEachObjectInYAML excutes actionFn for each object in the provided YAML.
  1621  // If an error is returned then no further objects are processed.
  1622  // The data may be a single YAML document or multidoc YAML.
  1623  // When a non-empty namespace is provided then all objects are assigned the
  1624  // the namespace prior to any other actions being performed with or to the
  1625  // object.
  1626  // this is heavily ispired by https://github.com/kubernetes/client-go/issues/216#issuecomment-718813670
  1627  func forEachObjectInYAML(
  1628  	monitor mntr.Monitor,
  1629  	data []byte,
  1630  	actionFn forEachObjectInYAMLActionFunc) error {
  1631  
  1632  	chanObj, chanErr := decodeYAML(data)
  1633  	for {
  1634  		select {
  1635  		case obj := <-chanObj:
  1636  			if obj == nil {
  1637  				return nil
  1638  			}
  1639  
  1640  			if err := actionFn(obj); err != nil {
  1641  				return fmt.Errorf("running passed forEachObjectInYAMLActionFunc failed: %w", err)
  1642  			}
  1643  			monitor.WithFields(map[string]interface{}{
  1644  				"kind": obj.GetKind(),
  1645  				"name": obj.GetName(),
  1646  			}).Debug("forEachObjectInYAMLActionFunc succeeded")
  1647  		case err := <-chanErr:
  1648  			if err == nil {
  1649  				return nil
  1650  			}
  1651  			return err
  1652  		}
  1653  	}
  1654  }
  1655  
  1656  // DecodeYAML unmarshals a YAML document or multidoc YAML as unstructured
  1657  // objects, placing each decoded object into a channel.
  1658  // this is heavily ispired by https://github.com/kubernetes/client-go/issues/216#issuecomment-718813670
  1659  func decodeYAML(data []byte) (<-chan *unstructured.Unstructured, <-chan error) {
  1660  
  1661  	var (
  1662  		chanErr        = make(chan error)
  1663  		chanObj        = make(chan *unstructured.Unstructured)
  1664  		multidocReader = utilyaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(data)))
  1665  	)
  1666  
  1667  	go func() {
  1668  		defer close(chanErr)
  1669  		defer close(chanObj)
  1670  
  1671  		// Iterate over the data until Read returns io.EOF. Every successful
  1672  		// read returns a complete YAML document.
  1673  		for {
  1674  			buf, err := multidocReader.Read()
  1675  			if err != nil {
  1676  				if err == io.EOF {
  1677  					return
  1678  				}
  1679  				chanErr <- fmt.Errorf("failed to read yaml data: %w", err)
  1680  				return
  1681  			}
  1682  
  1683  			// Define the unstructured object into which the YAML document will be
  1684  			// unmarshaled.
  1685  			obj := &unstructured.Unstructured{
  1686  				Object: map[string]interface{}{},
  1687  			}
  1688  
  1689  			// Unmarshal the YAML document into the unstructured object.
  1690  			if err := k8syaml.Unmarshal(buf, &obj.Object); err != nil {
  1691  				chanErr <- fmt.Errorf("failed to unmarshal yaml data: %w", err)
  1692  				return
  1693  			}
  1694  
  1695  			// filter non-kind resources
  1696  			if kind, ok := obj.Object["kind"]; !ok || kind == "" {
  1697  				continue
  1698  			}
  1699  
  1700  			// Place the unstructured object into the channel.
  1701  			chanObj <- obj
  1702  		}
  1703  	}()
  1704  
  1705  	return chanObj, chanErr
  1706  }