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  }