github.com/kubewharf/katalyst-core@v0.5.3/pkg/client/control/cnr.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  	jsonpatch "github.com/evanphx/json-patch"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/types"
    27  	"k8s.io/klog/v2"
    28  
    29  	"github.com/kubewharf/katalyst-api/pkg/apis/node/v1alpha1"
    30  	clientset "github.com/kubewharf/katalyst-api/pkg/client/clientset/versioned"
    31  )
    32  
    33  // CNRControl is used to update CNR
    34  type CNRControl interface {
    35  	// CreateCNR creates CNR from APIServer
    36  	CreateCNR(ctx context.Context, cnr *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error)
    37  
    38  	// DeleteCNR deletes CNR from APIServer
    39  	DeleteCNR(ctx context.Context, cnrName string) error
    40  
    41  	// PatchCNRSpecAndMetadata is used to update the changes for CNR Spec contents
    42  	PatchCNRSpecAndMetadata(ctx context.Context, cnrName string, oldCNR, newCNR *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error)
    43  
    44  	// PatchCNRStatus is used to update the changes for CNR Status contents
    45  	PatchCNRStatus(ctx context.Context, cnrName string, oldCNR, newCNR *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error)
    46  }
    47  
    48  type DummyCNRControl struct{}
    49  
    50  func (d DummyCNRControl) CreateCNR(_ context.Context, _ *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error) {
    51  	return nil, nil
    52  }
    53  
    54  func (d DummyCNRControl) DeleteCNR(_ context.Context, _ string) error {
    55  	return nil
    56  }
    57  
    58  func (d DummyCNRControl) PatchCNRSpecAndMetadata(_ context.Context, _ string, _, _ *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error) {
    59  	return nil, nil
    60  }
    61  
    62  func (d DummyCNRControl) PatchCNRStatus(_ context.Context, _ string, _, _ *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error) {
    63  	return nil, nil
    64  }
    65  
    66  var _ CNRControl = DummyCNRControl{}
    67  
    68  type CNRControlImpl struct {
    69  	client clientset.Interface
    70  }
    71  
    72  func NewCNRControlImpl(client clientset.Interface) *CNRControlImpl {
    73  	return &CNRControlImpl{
    74  		client: client,
    75  	}
    76  }
    77  
    78  func (c *CNRControlImpl) CreateCNR(ctx context.Context, cnr *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error) {
    79  	if cnr == nil {
    80  		return nil, fmt.Errorf("can't create a nil cnr")
    81  	}
    82  
    83  	return c.client.NodeV1alpha1().CustomNodeResources().Create(ctx, cnr, metav1.CreateOptions{})
    84  }
    85  
    86  func (c *CNRControlImpl) DeleteCNR(ctx context.Context, cnrName string) error {
    87  	return c.client.NodeV1alpha1().CustomNodeResources().Delete(ctx, cnrName, metav1.DeleteOptions{})
    88  }
    89  
    90  func (c *CNRControlImpl) PatchCNRSpecAndMetadata(ctx context.Context, cnrName string, oldCNR, newCNR *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error) {
    91  	patchBytes, err := preparePatchBytesForCNRSpecAndMetadata(cnrName, oldCNR, newCNR)
    92  	if err != nil {
    93  		klog.Errorf("prepare patch bytes for spec and metadata for cnr %q: %v", cnrName, err)
    94  		return nil, err
    95  	}
    96  
    97  	updatedCNR, err := c.client.NodeV1alpha1().CustomNodeResources().Patch(ctx, cnrName, types.MergePatchType, patchBytes, metav1.PatchOptions{})
    98  	if err != nil {
    99  		klog.Errorf("failed to patch spec and metadata %q for cnr %q: %v", patchBytes, cnrName, err)
   100  		return nil, err
   101  	}
   102  
   103  	return updatedCNR, nil
   104  }
   105  
   106  func (c *CNRControlImpl) PatchCNRStatus(ctx context.Context, cnrName string, oldCNR, newCNR *v1alpha1.CustomNodeResource) (*v1alpha1.CustomNodeResource, error) {
   107  	patchBytes, err := preparePatchBytesForCNRStatus(cnrName, oldCNR, newCNR)
   108  	if err != nil {
   109  		klog.Errorf("prepare patch bytes for status for cnr %q: %v", cnrName, err)
   110  		return nil, err
   111  	}
   112  
   113  	updatedCNR, err := c.client.NodeV1alpha1().CustomNodeResources().Patch(ctx, cnrName, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status")
   114  	if err != nil {
   115  		klog.Errorf("failed to patch status %q for cnr %q: %v", patchBytes, cnrName, err)
   116  		return nil, err
   117  	}
   118  
   119  	return updatedCNR, nil
   120  }
   121  
   122  // preparePatchBytesForCNRStatus generate those json patch bytes for comparing new and old CNR Status
   123  // while keep the Spec remains the same
   124  func preparePatchBytesForCNRStatus(cnrName string, oldCNR, newCNR *v1alpha1.CustomNodeResource) ([]byte, error) {
   125  	if oldCNR == nil || newCNR == nil {
   126  		return nil, fmt.Errorf("neither old nor new object can be nil")
   127  	}
   128  
   129  	oldData, err := json.Marshal(oldCNR)
   130  	if err != nil {
   131  		return nil, fmt.Errorf("failed to Marshal oldData for cnr %q: %v", cnrName, err)
   132  	}
   133  
   134  	diffCNR := oldCNR.DeepCopy()
   135  	diffCNR.Status = newCNR.Status
   136  	newData, err := json.Marshal(diffCNR)
   137  	if err != nil {
   138  		return nil, fmt.Errorf("failed to Marshal newData for cnr %q: %v", cnrName, err)
   139  	}
   140  
   141  	patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for cnr %q: %v", cnrName, err)
   144  	}
   145  
   146  	return patchBytes, nil
   147  }
   148  
   149  // preparePatchBytesForCNRStatus generate those json patch bytes for comparing new and old CNR Spec
   150  // while keep the Status remains the same
   151  func preparePatchBytesForCNRSpecAndMetadata(cnrName string, oldCNR, newCNR *v1alpha1.CustomNodeResource) ([]byte, error) {
   152  	if oldCNR == nil || newCNR == nil {
   153  		return nil, fmt.Errorf("neither old nor new object can be nil")
   154  	}
   155  
   156  	oldData, err := json.Marshal(oldCNR)
   157  	if err != nil {
   158  		return nil, fmt.Errorf("failed to Marshal oldData for cnr %q: %v", cnrName, err)
   159  	}
   160  
   161  	diffCNR := oldCNR.DeepCopy()
   162  	diffCNR.Spec = newCNR.Spec
   163  	diffCNR.ObjectMeta = newCNR.ObjectMeta
   164  	newData, err := json.Marshal(diffCNR)
   165  	if err != nil {
   166  		return nil, fmt.Errorf("failed to Marshal newData for cnr %q: %v", cnrName, err)
   167  	}
   168  
   169  	patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
   170  	if err != nil {
   171  		return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for cnr %q: %v", cnrName, err)
   172  	}
   173  
   174  	return patchBytes, nil
   175  }