github.com/docker/compose-on-kubernetes@v0.5.0/internal/controller/resourceupdater.go (about)

     1  package controller
     2  
     3  import (
     4  	"github.com/docker/compose-on-kubernetes/api/client/clientset"
     5  	"github.com/docker/compose-on-kubernetes/api/compose/latest"
     6  	"github.com/docker/compose-on-kubernetes/api/labels"
     7  	"github.com/docker/compose-on-kubernetes/internal/stackresources"
     8  	"github.com/docker/compose-on-kubernetes/internal/stackresources/diff"
     9  	"github.com/pkg/errors"
    10  	appstypes "k8s.io/api/apps/v1"
    11  	coretypes "k8s.io/api/core/v1"
    12  	kerrors "k8s.io/apimachinery/pkg/api/errors"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	k8sclientset "k8s.io/client-go/kubernetes"
    15  	"k8s.io/client-go/rest"
    16  )
    17  
    18  type impersonatingResourceUpdaterProvider struct {
    19  	config     rest.Config
    20  	ownerCache StackOwnerCacher
    21  }
    22  
    23  func (p *impersonatingResourceUpdaterProvider) getUpdater(stack *latest.Stack, isDirty bool) (resourceUpdater, error) {
    24  	ic, err := p.ownerCache.getWithRetries(stack, !isDirty)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	localConfig := p.config
    29  	localConfig.Impersonate = ic
    30  	result := &k8sResourceUpdater{
    31  		originalStack: stack,
    32  	}
    33  	if result.stackClient, err = clientset.NewForConfig(&localConfig); err != nil {
    34  		return nil, err
    35  	}
    36  	if result.k8sclient, err = k8sclientset.NewForConfig(&localConfig); err != nil {
    37  		return nil, err
    38  	}
    39  	return result, nil
    40  }
    41  
    42  // NewImpersonatingResourceUpdaterProvider creates a ResourceUpdaterProvider that impersonate api calls
    43  func NewImpersonatingResourceUpdaterProvider(config rest.Config, ownerCache StackOwnerCacher) ResourceUpdaterProvider {
    44  	return &impersonatingResourceUpdaterProvider{config: config, ownerCache: ownerCache}
    45  }
    46  
    47  var deletePolicy = metav1.DeletePropagationForeground
    48  var deleteOptions = metav1.DeleteOptions{
    49  	PropagationPolicy: &deletePolicy,
    50  }
    51  
    52  type k8sResourceUpdater struct {
    53  	stackClient   clientset.Interface
    54  	k8sclient     k8sclientset.Interface
    55  	originalStack *latest.Stack
    56  }
    57  
    58  func (u *k8sResourceUpdater) applyDaemonsets(toAdd, toUpdate, toDelete []appstypes.DaemonSet) error {
    59  	for _, r := range toDelete {
    60  		if err := u.k8sclient.AppsV1().DaemonSets(u.originalStack.Namespace).Delete(r.Name, &deleteOptions); err != nil && !kerrors.IsNotFound(err) {
    61  			return errors.Wrapf(err, "k8sResourceUpdater: error while deleting daemonset %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
    62  		}
    63  	}
    64  	for _, r := range toAdd {
    65  		if _, err := u.k8sclient.AppsV1().DaemonSets(u.originalStack.Namespace).Create(&r); err != nil {
    66  			return errors.Wrapf(err, "k8sResourceUpdater: error while creating daemonset %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
    67  		}
    68  	}
    69  	for _, r := range toUpdate {
    70  		if _, err := u.k8sclient.AppsV1().DaemonSets(u.originalStack.Namespace).Update(&r); err != nil {
    71  			return errors.Wrapf(err, "k8sResourceUpdater: error while patching daemonset %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  func (u *k8sResourceUpdater) applyDeployments(toAdd, toUpdate, toDelete []appstypes.Deployment) error {
    78  	for _, r := range toDelete {
    79  		if err := u.k8sclient.AppsV1().Deployments(u.originalStack.Namespace).Delete(r.Name, &deleteOptions); err != nil && !kerrors.IsNotFound(err) {
    80  			return errors.Wrapf(err, "k8sResourceUpdater: error while deleting deployment %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
    81  		}
    82  	}
    83  	for _, r := range toAdd {
    84  		if _, err := u.k8sclient.AppsV1().Deployments(u.originalStack.Namespace).Create(&r); err != nil {
    85  			return errors.Wrapf(err, "k8sResourceUpdater: error while creating deployment %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
    86  		}
    87  	}
    88  	for _, r := range toUpdate {
    89  		if _, err := u.k8sclient.AppsV1().Deployments(u.originalStack.Namespace).Update(&r); err != nil {
    90  			return errors.Wrapf(err, "k8sResourceUpdater: error while patching deployment %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
    91  		}
    92  	}
    93  	return nil
    94  }
    95  
    96  func (u *k8sResourceUpdater) applyStatefulsets(toAdd, toUpdate, toDelete []appstypes.StatefulSet) error {
    97  	for _, r := range toDelete {
    98  		if err := u.k8sclient.AppsV1().StatefulSets(u.originalStack.Namespace).Delete(r.Name, &deleteOptions); err != nil && !kerrors.IsNotFound(err) {
    99  			return errors.Wrapf(err, "k8sResourceUpdater: error while deleting statefulset %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   100  		}
   101  	}
   102  	for _, r := range toAdd {
   103  		if _, err := u.k8sclient.AppsV1().StatefulSets(u.originalStack.Namespace).Create(&r); err != nil {
   104  			return errors.Wrapf(err, "k8sResourceUpdater: error while creating statefulset %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   105  		}
   106  	}
   107  	for _, r := range toUpdate {
   108  		if _, err := u.k8sclient.AppsV1().StatefulSets(u.originalStack.Namespace).Update(&r); err != nil {
   109  			return errors.Wrapf(err, "k8sResourceUpdater: error while patching statefulset %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   110  		}
   111  	}
   112  	return nil
   113  }
   114  
   115  func (u *k8sResourceUpdater) applyServices(toAdd, toUpdate, toDelete []coretypes.Service) error {
   116  	for _, r := range toDelete {
   117  		if err := u.k8sclient.CoreV1().Services(u.originalStack.Namespace).Delete(r.Name, &deleteOptions); err != nil && !kerrors.IsNotFound(err) {
   118  			return errors.Wrapf(err, "k8sResourceUpdater: error while deleting service %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   119  		}
   120  	}
   121  	for _, r := range toAdd {
   122  		if _, err := u.k8sclient.CoreV1().Services(u.originalStack.Namespace).Create(&r); err != nil {
   123  			return errors.Wrapf(err, "k8sResourceUpdater: error while creating service %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   124  		}
   125  	}
   126  	for _, r := range toUpdate {
   127  		if _, err := u.k8sclient.CoreV1().Services(u.originalStack.Namespace).Update(&r); err != nil {
   128  			return errors.Wrapf(err, "k8sResourceUpdater: error while patching service %s in stack %s", r.Name, stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   129  		}
   130  	}
   131  	return nil
   132  }
   133  
   134  func (u *k8sResourceUpdater) applyStackDiff(d *diff.StackStateDiff) error {
   135  	if err := u.applyDaemonsets(d.DaemonsetsToAdd, d.DaemonsetsToUpdate, d.DaemonsetsToDelete); err != nil {
   136  		return err
   137  	}
   138  	if err := u.applyDeployments(d.DeploymentsToAdd, d.DeploymentsToUpdate, d.DeploymentsToDelete); err != nil {
   139  		return err
   140  	}
   141  	if err := u.applyStatefulsets(d.StatefulsetsToAdd, d.StatefulsetsToUpdate, d.StatefulsetsToDelete); err != nil {
   142  		return err
   143  	}
   144  	if err := u.applyServices(d.ServicesToAdd, d.ServicesToUpdate, d.ServicesToDelete); err != nil {
   145  		return err
   146  	}
   147  	return nil
   148  }
   149  
   150  func (u *k8sResourceUpdater) updateStackStatus(status latest.StackStatus) (*latest.Stack, error) {
   151  	if u.originalStack.Status != nil && *u.originalStack.Status == status {
   152  		return u.originalStack, nil
   153  	}
   154  	newStack := u.originalStack.Clone()
   155  	newStack.Status = &status
   156  	updated, err := u.stackClient.ComposeLatest().Stacks(u.originalStack.Namespace).WithSkipValidation().Update(newStack)
   157  	if err != nil {
   158  		return nil, errors.Wrapf(err, "k8sResourceUpdater: error while patching stack %s", stackresources.ObjKey(u.originalStack.Namespace, u.originalStack.Name))
   159  	}
   160  	return updated, nil
   161  }
   162  
   163  func (u *k8sResourceUpdater) deleteSecretsNoCollection() error {
   164  	if u.originalStack.Spec == nil {
   165  		return nil
   166  	}
   167  	for name, s := range u.originalStack.Spec.Secrets {
   168  		if s.External.External {
   169  			continue
   170  		}
   171  		if err := u.k8sclient.CoreV1().Secrets(u.originalStack.Namespace).Delete(name, nil); err != nil && !kerrors.IsNotFound(err) {
   172  			return err
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  func (u *k8sResourceUpdater) deleteConfigMapsNoCollection() error {
   179  	if u.originalStack.Spec == nil {
   180  		return nil
   181  	}
   182  	for name, s := range u.originalStack.Spec.Configs {
   183  		if s.External.External {
   184  			continue
   185  		}
   186  		if err := u.k8sclient.CoreV1().ConfigMaps(u.originalStack.Namespace).Delete(name, nil); err != nil && !kerrors.IsNotFound(err) {
   187  			return err
   188  		}
   189  	}
   190  	return nil
   191  }
   192  func (u *k8sResourceUpdater) deleteSecretsAndConfigMaps() error {
   193  	listOptions := metav1.ListOptions{
   194  		LabelSelector: labels.SelectorForStack(u.originalStack.Name),
   195  	}
   196  	if err := u.k8sclient.CoreV1().Secrets(u.originalStack.Namespace).DeleteCollection(nil, listOptions); err != nil {
   197  		if kerrors.IsForbidden(err) {
   198  			if err := u.deleteSecretsNoCollection(); err != nil {
   199  				return err
   200  			}
   201  		}
   202  	}
   203  	if err := u.k8sclient.CoreV1().ConfigMaps(u.originalStack.Namespace).DeleteCollection(nil, listOptions); err != nil {
   204  		if kerrors.IsForbidden(err) {
   205  			if err := u.deleteConfigMapsNoCollection(); err != nil {
   206  				return err
   207  			}
   208  		}
   209  	}
   210  	return nil
   211  }