github.com/kubewharf/katalyst-core@v0.5.3/pkg/client/control/cnc.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 27 jsonpatch "github.com/evanphx/json-patch" 28 29 "github.com/kubewharf/katalyst-api/pkg/apis/config/v1alpha1" 30 clientset "github.com/kubewharf/katalyst-api/pkg/client/clientset/versioned" 31 ) 32 33 // CNCControl is used to update CustomNodeConfig 34 // todo: use patch instead of update to avoid conflict 35 type CNCControl interface { 36 // CreateCNC is used to create new CNC obj 37 CreateCNC(ctx context.Context, cnc *v1alpha1.CustomNodeConfig, 38 opts metav1.CreateOptions) (*v1alpha1.CustomNodeConfig, error) 39 40 // DeleteCNC is used to delete CNC obj 41 DeleteCNC(ctx context.Context, cncName string, 42 opts metav1.DeleteOptions) error 43 44 // PatchCNC is used to update the changes for CNC spec and metadata contents 45 PatchCNC(ctx context.Context, cncName string, oldCNC, 46 newCNC *v1alpha1.CustomNodeConfig) (*v1alpha1.CustomNodeConfig, error) 47 48 // PatchCNCStatus is used to update the changes for CNC status contents 49 PatchCNCStatus(ctx context.Context, cncName string, oldCNC, 50 newCNC *v1alpha1.CustomNodeConfig) (*v1alpha1.CustomNodeConfig, error) 51 } 52 53 type DummyCNCControl struct{} 54 55 func (d DummyCNCControl) CreateCNC(_ context.Context, cnc *v1alpha1.CustomNodeConfig, 56 _ metav1.CreateOptions, 57 ) (*v1alpha1.CustomNodeConfig, error) { 58 return cnc, nil 59 } 60 61 func (d DummyCNCControl) DeleteCNC(_ context.Context, _ string, 62 _ metav1.DeleteOptions, 63 ) error { 64 return nil 65 } 66 67 func (d DummyCNCControl) PatchCNC(_ context.Context, _ string, 68 _, newCNC *v1alpha1.CustomNodeConfig, 69 ) (*v1alpha1.CustomNodeConfig, error) { 70 return newCNC, nil 71 } 72 73 func (d DummyCNCControl) PatchCNCStatus(_ context.Context, _ string, 74 _, newCNC *v1alpha1.CustomNodeConfig, 75 ) (*v1alpha1.CustomNodeConfig, error) { 76 return newCNC, nil 77 } 78 79 type RealCNCControl struct { 80 client clientset.Interface 81 } 82 83 func (r *RealCNCControl) CreateCNC(ctx context.Context, cnc *v1alpha1.CustomNodeConfig, opts metav1.CreateOptions) (*v1alpha1.CustomNodeConfig, error) { 84 if cnc == nil { 85 return nil, fmt.Errorf("can't create a nil cnc") 86 } 87 88 return r.client.ConfigV1alpha1().CustomNodeConfigs().Create(ctx, cnc, opts) 89 } 90 91 func (r *RealCNCControl) DeleteCNC(ctx context.Context, cncName string, opts metav1.DeleteOptions) error { 92 return r.client.ConfigV1alpha1().CustomNodeConfigs().Delete(ctx, cncName, opts) 93 } 94 95 func (r *RealCNCControl) PatchCNC(ctx context.Context, cncName string, oldCNC, newCNC *v1alpha1.CustomNodeConfig) (*v1alpha1.CustomNodeConfig, error) { 96 patchBytes, err := preparePatchBytesForCNC(cncName, oldCNC, newCNC) 97 if err != nil { 98 return nil, err 99 } 100 101 updatedCNC, err := r.client.ConfigV1alpha1().CustomNodeConfigs().Patch(ctx, cncName, types.MergePatchType, patchBytes, metav1.PatchOptions{}) 102 if err != nil { 103 return nil, fmt.Errorf("failed to patch spec and metadata %q for cnc %q: %v", patchBytes, cncName, err) 104 } 105 106 return updatedCNC, nil 107 } 108 109 func (r *RealCNCControl) PatchCNCStatus(ctx context.Context, cncName string, oldCNC, newCNC *v1alpha1.CustomNodeConfig) (*v1alpha1.CustomNodeConfig, error) { 110 patchBytes, err := preparePatchBytesForCNCStatus(cncName, oldCNC, newCNC) 111 if err != nil { 112 return nil, err 113 } 114 115 updatedCNC, err := r.client.ConfigV1alpha1().CustomNodeConfigs().Patch(ctx, cncName, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status") 116 if err != nil { 117 return nil, fmt.Errorf("failed to patch status %q for cnc %q: %v", patchBytes, cncName, err) 118 } 119 120 return updatedCNC, nil 121 } 122 123 // preparePatchBytesForCNCStatus generate those json patch bytes for comparing new and old CNC status 124 // while keep the spec remains the same 125 func preparePatchBytesForCNCStatus(cncName string, oldCNC, newCNC *v1alpha1.CustomNodeConfig) ([]byte, error) { 126 if oldCNC == nil || newCNC == nil { 127 return nil, fmt.Errorf("neither old nor new object can be nil") 128 } 129 130 oldData, err := json.Marshal(oldCNC) 131 if err != nil { 132 return nil, fmt.Errorf("failed to Marshal oldData for cnc %q: %v", cncName, err) 133 } 134 135 diffCNC := oldCNC.DeepCopy() 136 diffCNC.Status = newCNC.Status 137 newData, err := json.Marshal(diffCNC) 138 if err != nil { 139 return nil, fmt.Errorf("failed to Marshal newData for cnc %q: %v", cncName, err) 140 } 141 142 patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) 143 if err != nil { 144 return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for cnc %q: %v", cncName, err) 145 } 146 147 return patchBytes, nil 148 } 149 150 // preparePatchBytesForCNC generate those json patch bytes for comparing new and old CNC spec and metadata 151 // while keep the status remains the same 152 func preparePatchBytesForCNC(cncName string, oldCNC, newCNC *v1alpha1.CustomNodeConfig) ([]byte, error) { 153 if oldCNC == nil || newCNC == nil { 154 return nil, fmt.Errorf("neither old nor new object can be nil") 155 } 156 157 oldData, err := json.Marshal(oldCNC) 158 if err != nil { 159 return nil, fmt.Errorf("failed to Marshal oldData for cnc %q: %v", cncName, err) 160 } 161 162 diffCNC := oldCNC.DeepCopy() 163 diffCNC.Spec = newCNC.Spec 164 diffCNC.ObjectMeta = newCNC.ObjectMeta 165 newData, err := json.Marshal(diffCNC) 166 if err != nil { 167 return nil, fmt.Errorf("failed to Marshal newData for cnc %q: %v", cncName, err) 168 } 169 170 patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) 171 if err != nil { 172 return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for cnc %q: %v", cncName, err) 173 } 174 175 return patchBytes, nil 176 } 177 178 func NewRealCNCControl(client clientset.Interface) *RealCNCControl { 179 return &RealCNCControl{ 180 client: client, 181 } 182 }