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

     1  package registry
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/docker/compose-on-kubernetes/api/compose/latest"
     8  	"github.com/docker/compose-on-kubernetes/internal/conversions"
     9  	"github.com/docker/compose-on-kubernetes/internal/convert"
    10  	iv "github.com/docker/compose-on-kubernetes/internal/internalversion"
    11  	"github.com/docker/compose-on-kubernetes/internal/stackresources"
    12  	log "github.com/sirupsen/logrus"
    13  	coretypes "k8s.io/api/core/v1"
    14  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/runtime"
    17  	"k8s.io/apiserver/pkg/registry/rest"
    18  	appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
    19  	restclient "k8s.io/client-go/rest"
    20  )
    21  
    22  type stackScaleRest struct {
    23  	storage stackRESTStore
    24  	config  *restclient.Config
    25  }
    26  
    27  var _ rest.Storage = &stackScaleRest{}
    28  var _ rest.Getter = &stackScaleRest{}
    29  var _ rest.Updater = &stackScaleRest{}
    30  
    31  // NewStackScaleRest returns a rest storage for scale subresource
    32  func NewStackScaleRest(store stackRESTStore, config *restclient.Config) rest.Storage {
    33  	return &stackScaleRest{storage: store, config: config}
    34  }
    35  
    36  func (r *stackScaleRest) New() runtime.Object {
    37  	return &latest.Scale{}
    38  }
    39  
    40  func (r *stackScaleRest) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
    41  	stack, err := r.storage.GetStack(ctx, name, options)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	res := latest.Scale{
    46  		Spec:   make(map[string]int),
    47  		Status: make(map[string]int),
    48  	}
    49  
    50  	for _, s := range stack.Spec.Stack.Services {
    51  		count := 1
    52  		switch {
    53  		case s.Deploy.Mode == "global":
    54  			count = -1
    55  		case s.Deploy.Replicas != nil:
    56  			count = int(*s.Deploy.Replicas)
    57  		}
    58  		res.Spec[s.Name] = count
    59  	}
    60  
    61  	apps, err := appsv1.NewForConfig(r.config)
    62  	if err != nil {
    63  		log.Errorf("Failed to get apps: %s", err)
    64  		return nil, err
    65  	}
    66  	stackLatest, err := conversions.StackFromInternalV1alpha3(stack)
    67  	if err != nil {
    68  		log.Errorf("Failed to convert to StackDefinition: %s", err)
    69  		return nil, err
    70  	}
    71  	strategy, err := convert.ServiceStrategyFor(coretypes.ServiceTypeLoadBalancer) // in that case, service strategy does not really matter
    72  	if err != nil {
    73  		log.Errorf("Failed to convert to StackDefinition: %s", err)
    74  		return nil, err
    75  	}
    76  	stackDef, err := convert.StackToStack(*stackLatest, strategy, stackresources.EmptyStackState)
    77  	if err != nil {
    78  		log.Errorf("Failed to convert to StackDefinition: %s", err)
    79  		return nil, err
    80  	}
    81  	for _, v := range stackDef.Deployments {
    82  		dep, err := apps.Deployments(stack.Namespace).Get(v.Name, metav1.GetOptions{})
    83  		if err != nil {
    84  			log.Errorf("Failed to get Deployment for %s: %s", v.Name, err)
    85  		} else {
    86  			res.Status[v.Name] = int(dep.Status.AvailableReplicas)
    87  		}
    88  	}
    89  	for _, v := range stackDef.Statefulsets {
    90  		ss, err := apps.StatefulSets(stack.Namespace).Get(v.Name, metav1.GetOptions{})
    91  		if err != nil {
    92  			log.Errorf("Failed to get StatefulSet for %s: %s", v.Name, err)
    93  		} else {
    94  			res.Status[v.Name] = int(ss.Status.ReadyReplicas)
    95  		}
    96  	}
    97  	for _, v := range stackDef.Daemonsets {
    98  		ds, err := apps.DaemonSets(stack.Namespace).Get(v.Name, metav1.GetOptions{})
    99  		if err != nil {
   100  			log.Errorf("Failed to get StatefulSet for %s: %s", v.Name, err)
   101  		} else {
   102  			res.Status[v.Name] = int(ds.Status.NumberAvailable)
   103  		}
   104  	}
   105  	return &res, nil
   106  }
   107  
   108  func (r *stackScaleRest) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo,
   109  	createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
   110  	scale, err := r.Get(ctx, name, &metav1.GetOptions{})
   111  	if err != nil {
   112  		return nil, false, err
   113  	}
   114  	newScalero, err := objInfo.UpdatedObject(ctx, scale)
   115  	if err != nil {
   116  		return nil, false, err
   117  	}
   118  	newScale := newScalero.(*latest.Scale)
   119  	log.Infof("Scale update %s: %v", name, newScale.Spec)
   120  
   121  	return r.storage.UpdateStack(ctx, name, func(ctx context.Context, newObj *iv.Stack, oldObj *iv.Stack) (transformedNewObj *iv.Stack, err error) {
   122  		for target, count := range newScale.Spec {
   123  			r := uint64(count)
   124  			hit := false
   125  			for i, srv := range newObj.Spec.Stack.Services {
   126  				if srv.Name == target {
   127  					newObj.Spec.Stack.Services[i].Deploy.Replicas = &r
   128  					hit = true
   129  					break
   130  				}
   131  			}
   132  			if !hit {
   133  				return nil, fmt.Errorf("service %q not found in stack", target)
   134  			}
   135  		}
   136  		if !apiequality.Semantic.DeepEqual(oldObj.Spec, newObj.Spec) {
   137  			newObj.Generation = oldObj.Generation + 1
   138  		}
   139  		return newObj, nil
   140  	}, createValidation, updateValidation, forceAllowCreate, options)
   141  }