Skip to content

Commit 8852a89

Browse files
committed
fix tests
1 parent e94ecd0 commit 8852a89

File tree

2 files changed

+225
-1
lines changed

2 files changed

+225
-1
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package harvester
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/kubernetes-sigs/kubebuilder/pkg/controller"
10+
"github.com/kubernetes-sigs/kubebuilder/pkg/controller/types"
11+
12+
"github.com/kubernetes-sigs/kubebuilder/pkg/controller/eventhandlers"
13+
"github.com/kubernetes-sigs/kubebuilder/pkg/controller/predicates"
14+
"github.com/lwolf/kubereplay/helpers"
15+
kubereplayv1alpha1 "github.com/lwolf/kubereplay/pkg/apis/kubereplay/v1alpha1"
16+
kubereplayv1alpha1client "github.com/lwolf/kubereplay/pkg/client/clientset/versioned/typed/kubereplay/v1alpha1"
17+
kubereplayv1alpha1informer "github.com/lwolf/kubereplay/pkg/client/informers/externalversions/kubereplay/v1alpha1"
18+
kubereplayv1alpha1lister "github.com/lwolf/kubereplay/pkg/client/listers/kubereplay/v1alpha1"
19+
"github.com/lwolf/kubereplay/pkg/inject/args"
20+
appsv1beta "k8s.io/api/apps/v1"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"k8s.io/client-go/kubernetes"
23+
appsv1lister "k8s.io/client-go/listers/apps/v1"
24+
"k8s.io/client-go/tools/record"
25+
"k8s.io/client-go/util/retry"
26+
)
27+
28+
const controllerAgentName = "kubereplay-harvester-controller"
29+
30+
func (bc *HarvesterController) reconcileDeployment(green *appsv1beta.Deployment, blue *appsv1beta.Deployment, blueReplicas int32, greenReplicas int32) {
31+
log.Printf("reconciling deployment %s to %d/%d", green.Name, blueReplicas, greenReplicas)
32+
if *blue.Spec.Replicas != blueReplicas {
33+
log.Printf("blue replica needs reconcilation %d != %d", *blue.Spec.Replicas, blueReplicas)
34+
deploy, err := bc.kubernetesclient.ExtensionsV1beta1().Deployments(blue.Namespace).Get(blue.Name, metav1.GetOptions{})
35+
if err != nil {
36+
log.Printf("failed to get scale for deployment %s: %v", blue.Name, err)
37+
}
38+
deploy.Spec.Replicas = &blueReplicas
39+
deploy.Annotations[helpers.AnnotationKeyReplicas] = fmt.Sprintf("%d", blueReplicas)
40+
_, err = bc.kubernetesclient.ExtensionsV1beta1().Deployments(blue.Namespace).Update(deploy)
41+
if err != nil {
42+
log.Printf("failed to scale deployment %s to %d replicas: %v", blue.Name, blueReplicas, err)
43+
}
44+
}
45+
if *green.Spec.Replicas != greenReplicas {
46+
log.Printf("green replica needs reconcilation %d != %d", *green.Spec.Replicas, greenReplicas)
47+
deploy, err := bc.kubernetesclient.ExtensionsV1beta1().Deployments(green.Namespace).Get(green.Name, metav1.GetOptions{})
48+
if err != nil {
49+
log.Printf("failed to get scale for deployment %s: %v", green.Name, err)
50+
}
51+
deploy.Spec.Replicas = &greenReplicas
52+
deploy.Annotations[helpers.AnnotationKeyReplicas] = fmt.Sprintf("%d", greenReplicas)
53+
_, err = bc.kubernetesclient.ExtensionsV1beta1().Deployments(green.Namespace).Update(deploy)
54+
if err != nil {
55+
log.Printf("failed to scale deployment %s to %d replicas: %v", green.Name, greenReplicas, err)
56+
}
57+
}
58+
59+
}
60+
61+
func arrayToMap(deployments []*appsv1beta.Deployment) map[string]*appsv1beta.Deployment {
62+
res := make(map[string]*appsv1beta.Deployment)
63+
for _, d := range deployments {
64+
res[d.Name] = d
65+
}
66+
return res
67+
}
68+
69+
func findOrphans(deployments map[string]*appsv1beta.Deployment) []string {
70+
var orphans []string
71+
for _, d := range deployments {
72+
if strings.HasSuffix(d.Name, "-gor") {
73+
_, ok := deployments[strings.TrimSuffix(d.Name, "-gor")]
74+
if !ok {
75+
orphans = append(orphans, d.Name)
76+
}
77+
}
78+
}
79+
return orphans
80+
}
81+
82+
func (bc *HarvesterController) Reconcile(k types.ReconcileKey) error {
83+
log.Printf("running reconcile Harvester for %s", k.Name)
84+
h, err := bc.Get(k.Namespace, k.Name)
85+
if err != nil {
86+
return err
87+
}
88+
89+
selector, err := metav1.LabelSelectorAsSelector(
90+
&metav1.LabelSelector{MatchLabels: h.Spec.Selector},
91+
)
92+
deploys, err := bc.deploymentLister.List(selector)
93+
if err != nil {
94+
return err
95+
}
96+
deploysMap := arrayToMap(deploys)
97+
orphans := findOrphans(deploysMap)
98+
propagationPolicy := metav1.DeletePropagationBackground
99+
for _, orph := range orphans {
100+
err = bc.kubernetesclient.ExtensionsV1beta1().Deployments(k.Namespace).Delete(
101+
orph, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy})
102+
if err != nil {
103+
log.Printf("failed to delete shadow deployment %s: %v", orph, err)
104+
}
105+
106+
}
107+
108+
var forceReconcile bool
109+
if h.Spec.SegmentSize != h.Status.SegmentSize {
110+
forceReconcile = true
111+
}
112+
113+
for _, d := range deploys {
114+
a, ok := d.Annotations[helpers.AnnotationKeyDefault]
115+
116+
if !ok {
117+
// annotation is not present, skipping
118+
continue
119+
}
120+
if a == helpers.AnnotationValueCapture {
121+
continue
122+
}
123+
blueName, ok := d.Annotations[helpers.AnnotationKeyShadow]
124+
if !ok {
125+
log.Printf("deployment %s does not have a shadow", d.Name)
126+
continue
127+
}
128+
blue, err := bc.deploymentLister.Deployments(d.Namespace).Get(blueName)
129+
if err != nil {
130+
log.Printf("failed to get deployment by shadow name %s: %v", blueName, err)
131+
continue
132+
}
133+
var blueReplicas, greenReplicas int32
134+
if forceReconcile {
135+
blueReplicas, greenReplicas = helpers.BlueGreenReplicas(*d.Spec.Replicas+*blue.Spec.Replicas, int32(h.Spec.SegmentSize))
136+
} else {
137+
ar, ok := d.Annotations[helpers.AnnotationKeyReplicas]
138+
if ok {
139+
v, err := strconv.Atoi(ar)
140+
if err == nil {
141+
if *d.Spec.Replicas == int32(v) {
142+
continue
143+
}
144+
}
145+
}
146+
blueReplicas, greenReplicas = helpers.BlueGreenReplicas(*d.Spec.Replicas, int32(h.Spec.SegmentSize))
147+
}
148+
log.Printf("new replicas count %d, %d. is forced reconcile=%v", blueReplicas, greenReplicas, forceReconcile)
149+
go bc.reconcileDeployment(d, blue, blueReplicas, greenReplicas)
150+
}
151+
152+
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
153+
// Retrieve the latest version of Harvester before attempting update
154+
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
155+
156+
result, getErr := bc.Get(h.Namespace, h.Name)
157+
if getErr != nil {
158+
log.Fatalf("Failed to get latest version of Harvester: %v", getErr)
159+
}
160+
result.Status.SegmentSize = h.Spec.SegmentSize
161+
_, updateErr := bc.harvesterclient.Harvesters(h.Namespace).Update(h)
162+
return updateErr
163+
})
164+
if retryErr != nil {
165+
log.Printf("Update failed: %v", retryErr)
166+
return retryErr
167+
}
168+
169+
log.Printf("Finished processing harvester...")
170+
return nil
171+
}
172+
173+
func (bc *HarvesterController) Lookup(k types.ReconcileKey) (interface{}, error) {
174+
return bc.harvesterLister.Harvesters(k.Namespace).Get(k.Name)
175+
}
176+
177+
func (bc *HarvesterController) Get(namespace, name string) (*kubereplayv1alpha1.Harvester, error) {
178+
return bc.harvesterLister.Harvesters(namespace).Get(name)
179+
}
180+
181+
// +controller:group=kubereplay,version=v1alpha1,kind=Harvester,resource=harvesters
182+
// +informers:group=apps,version=v1,kind=Deployment
183+
// +rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
184+
type HarvesterController struct {
185+
args.InjectArgs
186+
harvesterLister kubereplayv1alpha1lister.HarvesterLister
187+
harvesterclient kubereplayv1alpha1client.KubereplayV1alpha1Interface
188+
189+
deploymentLister appsv1lister.DeploymentLister
190+
kubernetesclient kubernetes.Interface
191+
192+
recorder record.EventRecorder
193+
}
194+
195+
// ProvideController provides a controller that will be run at startup. Kubebuilder will use codegeneration
196+
// to automatically register this controller in the inject package
197+
func ProvideController(arguments args.InjectArgs) (*controller.GenericController, error) {
198+
bc := &HarvesterController{
199+
InjectArgs: arguments,
200+
harvesterLister: arguments.ControllerManager.GetInformerProvider(&kubereplayv1alpha1.Harvester{}).(kubereplayv1alpha1informer.HarvesterInformer).Lister(),
201+
harvesterclient: arguments.Clientset.KubereplayV1alpha1(),
202+
deploymentLister: arguments.KubernetesInformers.Apps().V1().Deployments().Lister(),
203+
kubernetesclient: arguments.KubernetesClientSet,
204+
recorder: arguments.CreateRecorder(controllerAgentName),
205+
}
206+
207+
// Create a new controller that will call HarvesterController.Reconcile on changes to Harvesters
208+
gc := &controller.GenericController{
209+
Name: "HarvesterController",
210+
Reconcile: bc.Reconcile,
211+
InformerRegistry: arguments.ControllerManager,
212+
}
213+
if err := gc.Watch(&kubereplayv1alpha1.Harvester{}); err != nil {
214+
return gc, err
215+
}
216+
217+
// INSERT ADDITIONAL WATCHES HERE BY CALLING gc.Watch.*() FUNCTIONS
218+
// NOTE: Informers for Kubernetes resources *MUST* be registered in the pkg/inject package so that they are started.
219+
if err := gc.WatchControllerOf(&appsv1beta.Deployment{}, eventhandlers.Path{bc.Lookup},
220+
predicates.ResourceVersionChanged); err != nil {
221+
return gc, err
222+
}
223+
return gc, nil
224+
}

pkg/controller/refinery/controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ type RefineryController struct {
123123
refineryLister kubereplayv1alpha1lister.RefineryLister
124124
refineryclient kubereplayv1alpha1client.KubereplayV1alpha1Interface
125125
deploymentLister appsv1lister.DeploymentLister
126-
kubernetesclient *kubernetes.Clientset
126+
kubernetesclient kubernetes.Interface
127127

128128
recorder record.EventRecorder
129129
}

0 commit comments

Comments
 (0)