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 }