github.com/kubewharf/katalyst-core@v0.5.3/pkg/client/control/vpa.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package control 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/types" 26 "k8s.io/apimachinery/pkg/util/jsonmergepatch" 27 "k8s.io/klog/v2" 28 29 apis "github.com/kubewharf/katalyst-api/pkg/apis/autoscaling/v1alpha1" 30 clientset "github.com/kubewharf/katalyst-api/pkg/client/clientset/versioned" 31 "github.com/kubewharf/katalyst-core/pkg/util/general" 32 ) 33 34 // VPAUpdater is used to update VPA CR 35 // todo: use patch instead of update to avoid conflict 36 type VPAUpdater interface { 37 UpdateVPA(ctx context.Context, vpa *apis.KatalystVerticalPodAutoscaler, 38 opts metav1.UpdateOptions) (*apis.KatalystVerticalPodAutoscaler, error) 39 UpdateVPAStatus(ctx context.Context, vpa *apis.KatalystVerticalPodAutoscaler, 40 opts metav1.UpdateOptions) (*apis.KatalystVerticalPodAutoscaler, error) 41 PatchVPA(ctx context.Context, oldVPA, 42 newVPA *apis.KatalystVerticalPodAutoscaler) (*apis.KatalystVerticalPodAutoscaler, error) 43 PatchVPAStatus(ctx context.Context, oldVPA, 44 newVPA *apis.KatalystVerticalPodAutoscaler) (*apis.KatalystVerticalPodAutoscaler, error) 45 } 46 47 type DummyVPAUpdater struct{} 48 49 func (d *DummyVPAUpdater) UpdateVPA(_ context.Context, 50 _ *apis.KatalystVerticalPodAutoscaler, _ metav1.UpdateOptions, 51 ) (*apis.KatalystVerticalPodAutoscaler, error) { 52 return nil, nil 53 } 54 55 func (d *DummyVPAUpdater) UpdateVPAStatus(_ context.Context, 56 _ *apis.KatalystVerticalPodAutoscaler, _ metav1.UpdateOptions, 57 ) (*apis.KatalystVerticalPodAutoscaler, error) { 58 return nil, nil 59 } 60 61 func (d *DummyVPAUpdater) PatchVPA(_ context.Context, _, 62 newVPA *apis.KatalystVerticalPodAutoscaler, 63 ) (*apis.KatalystVerticalPodAutoscaler, error) { 64 return newVPA, nil 65 } 66 67 func (d *DummyVPAUpdater) PatchVPAStatus(_ context.Context, _, 68 newVPA *apis.KatalystVerticalPodAutoscaler, 69 ) (*apis.KatalystVerticalPodAutoscaler, error) { 70 return newVPA, nil 71 } 72 73 type RealVPAUpdater struct { 74 client clientset.Interface 75 } 76 77 func NewRealVPAUpdater(client clientset.Interface) VPAUpdater { 78 return &RealVPAUpdater{ 79 client: client, 80 } 81 } 82 83 func (r *RealVPAUpdater) UpdateVPA(ctx context.Context, vpa *apis.KatalystVerticalPodAutoscaler, 84 opts metav1.UpdateOptions, 85 ) (*apis.KatalystVerticalPodAutoscaler, error) { 86 if vpa == nil { 87 return nil, fmt.Errorf("can't update a nil vpa") 88 } 89 90 return r.client.AutoscalingV1alpha1().KatalystVerticalPodAutoscalers(vpa.Namespace).Update( 91 ctx, vpa, opts) 92 } 93 94 func (r *RealVPAUpdater) UpdateVPAStatus(ctx context.Context, vpa *apis.KatalystVerticalPodAutoscaler, 95 opts metav1.UpdateOptions, 96 ) (*apis.KatalystVerticalPodAutoscaler, error) { 97 if vpa == nil { 98 return nil, fmt.Errorf("can't update a nil vpa's status") 99 } 100 101 return r.client.AutoscalingV1alpha1().KatalystVerticalPodAutoscalers(vpa.Namespace).UpdateStatus( 102 ctx, vpa, opts) 103 } 104 105 func (r *RealVPAUpdater) PatchVPA(ctx context.Context, oldVPA, 106 newVPA *apis.KatalystVerticalPodAutoscaler, 107 ) (*apis.KatalystVerticalPodAutoscaler, error) { 108 if oldVPA == nil || newVPA == nil { 109 return nil, fmt.Errorf("can't patch a nil vpa") 110 } 111 112 oldData, err := json.Marshal(oldVPA) 113 if err != nil { 114 return nil, err 115 } 116 117 newData, err := json.Marshal(newVPA) 118 if err != nil { 119 return nil, err 120 } 121 122 patchBytes, err := jsonmergepatch.CreateThreeWayJSONMergePatch(oldData, newData, oldData) 123 if err != nil { 124 return nil, fmt.Errorf("failed to create merge patch for vpa %q/%q: %v", 125 oldVPA.Namespace, oldVPA.Name, err) 126 } else if general.JsonPathEmpty(patchBytes) { 127 return newVPA, nil 128 } 129 130 return r.client.AutoscalingV1alpha1().KatalystVerticalPodAutoscalers(oldVPA.Namespace). 131 Patch(ctx, oldVPA.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) 132 } 133 134 func (r *RealVPAUpdater) PatchVPAStatus(ctx context.Context, oldVPA, 135 newVPA *apis.KatalystVerticalPodAutoscaler, 136 ) (*apis.KatalystVerticalPodAutoscaler, error) { 137 if oldVPA == nil || newVPA == nil { 138 return nil, fmt.Errorf("can't patch a nil vpa") 139 } 140 141 oldData, err := json.Marshal(apis.KatalystVerticalPodAutoscaler{Status: oldVPA.Status}) 142 if err != nil { 143 return nil, err 144 } 145 146 newData, err := json.Marshal(apis.KatalystVerticalPodAutoscaler{Status: newVPA.Status}) 147 if err != nil { 148 return nil, err 149 } 150 151 if klog.V(5).Enabled() { 152 d, _ := json.Marshal(&oldVPA.Status) 153 dn, _ := json.Marshal(&newVPA.Status) 154 klog.Infof("vpa %s status updated: %v => %v", oldVPA.Name, string(d), string(dn)) 155 } 156 157 patchBytes, err := jsonmergepatch.CreateThreeWayJSONMergePatch(oldData, newData, oldData) 158 if err != nil { 159 return nil, fmt.Errorf("failed to create merge patch for vpa %q/%q: %v", 160 oldVPA.Namespace, oldVPA.Name, err) 161 } else if general.JsonPathEmpty(patchBytes) { 162 return newVPA, nil 163 } 164 165 return r.client.AutoscalingV1alpha1().KatalystVerticalPodAutoscalers(oldVPA.Namespace). 166 Patch(ctx, oldVPA.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status") 167 } 168 169 // RealVPAUpdaterWithMetric todo: implement with emitting metrics on updating 170 type RealVPAUpdaterWithMetric struct { 171 client clientset.Interface 172 RealVPAUpdater 173 }