mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-23 22:26:49 +00:00
Add create,update and delete placeManifestWork (#177)
Signed-off-by: melserngawy <melserng@redhat.com>
This commit is contained in:
@@ -14,6 +14,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201
|
||||
k8s.io/api v0.24.3
|
||||
k8s.io/apiextensions-apiserver v0.24.3
|
||||
k8s.io/apimachinery v0.24.3
|
||||
@@ -22,7 +23,7 @@ require (
|
||||
k8s.io/component-base v0.24.3
|
||||
k8s.io/klog/v2 v2.70.1
|
||||
k8s.io/utils v0.0.0-20220725171434-9bab9ef40391
|
||||
open-cluster-management.io/api v0.9.1-0.20230113024003-6529d52caeeb
|
||||
open-cluster-management.io/api v0.9.1-0.20230119012002-efddffc71682
|
||||
sigs.k8s.io/controller-runtime v0.12.3
|
||||
)
|
||||
|
||||
|
||||
@@ -738,6 +738,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE=
|
||||
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -1241,8 +1243,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
open-cluster-management.io/api v0.9.1-0.20230113024003-6529d52caeeb h1:I4YWWnSpMirqj/heYbu44WeKBK3MJGboLgr4MEJfygs=
|
||||
open-cluster-management.io/api v0.9.1-0.20230113024003-6529d52caeeb/go.mod h1:6BB/Y6r3hXlPjpJgDwIs6Ubxyx/kXXOg6D9Cntg1I9E=
|
||||
open-cluster-management.io/api v0.9.1-0.20230119012002-efddffc71682 h1:ty0A+e5vs8a3WiG+8uL4o3fyW91BzR7do9kD1vwNSxg=
|
||||
open-cluster-management.io/api v0.9.1-0.20230119012002-efddffc71682/go.mod h1:6BB/Y6r3hXlPjpJgDwIs6Ubxyx/kXXOg6D9Cntg1I9E=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
+14
-22
@@ -28,7 +28,7 @@ import (
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/klog/v2"
|
||||
clusterv1beta1client "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta1"
|
||||
clusterlister "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1"
|
||||
workv1client "open-cluster-management.io/api/client/work/clientset/versioned/typed/work/v1"
|
||||
clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
workapiv1 "open-cluster-management.io/api/work/v1"
|
||||
@@ -327,17 +327,24 @@ func GuessObjectGroupVersionKind(object runtime.Object) (*schema.GroupVersionKin
|
||||
}
|
||||
|
||||
// RemoveFinalizer removes a finalizer from the list. It mutates its input.
|
||||
func RemoveFinalizer(object runtime.Object, finalizerName string) {
|
||||
accessor, _ := meta.Accessor(object)
|
||||
func RemoveFinalizer(object runtime.Object, finalizerName string) (finalizersUpdated bool) {
|
||||
accessor, err := meta.Accessor(object)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
finalizers := accessor.GetFinalizers()
|
||||
newFinalizers := []string{}
|
||||
for i := range finalizers {
|
||||
if finalizers[i] == finalizerName {
|
||||
finalizersUpdated = true
|
||||
continue
|
||||
}
|
||||
newFinalizers = append(newFinalizers, finalizers[i])
|
||||
}
|
||||
accessor.SetFinalizers(newFinalizers)
|
||||
|
||||
return finalizersUpdated
|
||||
}
|
||||
|
||||
// AppliedManifestworkQueueKeyFunc return manifestwork key from appliedmanifestwork
|
||||
@@ -530,30 +537,15 @@ func BuildResourceMeta(
|
||||
}
|
||||
|
||||
type PlacementDecisionGetter struct {
|
||||
Client clusterv1beta1client.ClusterV1beta1Client
|
||||
Client clusterlister.PlacementDecisionLister
|
||||
}
|
||||
|
||||
func (pdl PlacementDecisionGetter) List(selector labels.Selector, namespace string) ([]*clusterv1beta1.PlacementDecision, error) {
|
||||
opts := metav1.ListOptions{
|
||||
LabelSelector: selector.String(),
|
||||
}
|
||||
|
||||
decisionList, err := pdl.Client.PlacementDecisions(namespace).List(context.Background(), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var decisions []*clusterv1beta1.PlacementDecision
|
||||
for i := range decisionList.Items {
|
||||
decisions = append(decisions, &decisionList.Items[i])
|
||||
}
|
||||
return decisions, nil
|
||||
return pdl.Client.PlacementDecisions(namespace).List(selector)
|
||||
}
|
||||
|
||||
func GetPlacement(client clusterv1beta1client.ClusterV1beta1Client, ctx context.Context, opts metav1.GetOptions, name string, ns string) (*clusterv1beta1.Placement, error) {
|
||||
return client.Placements(ns).Get(ctx, name, opts)
|
||||
}
|
||||
|
||||
func GetClusters(client clusterv1beta1client.ClusterV1beta1Client, placement *clusterv1beta1.Placement, existingClusters sets.String) (sets.String, sets.String, error) {
|
||||
// Get added and deleted clusters names
|
||||
func GetClusters(client clusterlister.PlacementDecisionLister, placement *clusterv1beta1.Placement, existingClusters sets.String) (sets.String, sets.String, error) {
|
||||
pdtracker := clusterv1beta1.NewPlacementDecisionClustersTracker(placement, PlacementDecisionGetter{Client: client}, existingClusters)
|
||||
|
||||
return pdtracker.Get()
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"golang.org/x/exp/slices"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
fakeclient "open-cluster-management.io/api/client/work/clientset/versioned/fake"
|
||||
helpertest "open-cluster-management.io/work/pkg/hub/test"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test add finalizer reconcile
|
||||
func TestAddFinalizerReconcile(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
fakeClient := fakeclient.NewSimpleClientset(pmwTest)
|
||||
|
||||
addFinalizerController := &addFinalizerReconciler{
|
||||
workClient: fakeClient,
|
||||
}
|
||||
|
||||
pmwTest, _, err := addFinalizerController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !slices.Contains(pmwTest.Finalizers, PlaceManifestWorkFinalizer) {
|
||||
t.Fatal("Finalizer did not added")
|
||||
}
|
||||
|
||||
// Set placeManifestWork delete time AND remove finalizer
|
||||
timeNow := metav1.Now()
|
||||
pmwTest.DeletionTimestamp = &timeNow
|
||||
pmwTest.Finalizers = []string{}
|
||||
|
||||
// Run reconcile
|
||||
pmwTest, _, err = addFinalizerController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check finalizer not exist
|
||||
if slices.Contains(pmwTest.Finalizers, PlaceManifestWorkFinalizer) {
|
||||
t.Fatal("Finalizer added to deleted placemanifestWork")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddFinalizerTwiceReconcile(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
pmwTest.Finalizers = []string{PlaceManifestWorkFinalizer}
|
||||
fakeClient := fakeclient.NewSimpleClientset(pmwTest)
|
||||
|
||||
addFinalizerController := &addFinalizerReconciler{
|
||||
workClient: fakeClient,
|
||||
}
|
||||
|
||||
pmwTest, _, err := addFinalizerController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check fakeClient Actions does not have update action
|
||||
if len(fakeClient.Actions()) > 0 {
|
||||
t.Fatal("PlaceMW finalizer should not be updated")
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
workinformerv1 "open-cluster-management.io/api/client/work/informers/externalversions/work/v1"
|
||||
workinformerv1alpha1 "open-cluster-management.io/api/client/work/informers/externalversions/work/v1alpha1"
|
||||
worklisterv1alpha1 "open-cluster-management.io/api/client/work/listers/work/v1alpha1"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -71,10 +72,12 @@ func NewPlaceManifestWorkController(
|
||||
placeManifestWorkIndexer: placeManifestWorkInformer.Informer().GetIndexer(),
|
||||
|
||||
reconcilers: []placeManifestWorkReconcile{
|
||||
&finalizeReconciler{workClient: workClient, manifestWorkLister: manifestWorkInformer.Lister()},
|
||||
&finalizeReconciler{workApplier: workapplier.NewWorkApplierWithTypedClient(workClient, manifestWorkInformer.Lister()),
|
||||
workClient: workClient, manifestWorkLister: manifestWorkInformer.Lister()},
|
||||
&addFinalizerReconciler{workClient: workClient},
|
||||
&deployReconciler{workClient: workClient, manifestWorkLister: manifestWorkInformer.Lister(), placementLister: placementInformer.Lister(), placeDecisionLister: placeDecisionInformer.Lister()},
|
||||
&statusReconciler{workClient: workClient, manifestWorkLister: manifestWorkInformer.Lister(), placeDecisionLister: placeDecisionInformer.Lister()},
|
||||
&deployReconciler{workApplier: workapplier.NewWorkApplierWithTypedClient(workClient, manifestWorkInformer.Lister()),
|
||||
manifestWorkLister: manifestWorkInformer.Lister(), placementLister: placementInformer.Lister(), placeDecisionLister: placeDecisionInformer.Lister()},
|
||||
&statusReconciler{manifestWorkLister: manifestWorkInformer.Lister()},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
fakeclusterclient "open-cluster-management.io/api/client/cluster/clientset/versioned/fake"
|
||||
clusterinformers "open-cluster-management.io/api/client/cluster/informers/externalversions"
|
||||
fakeworkclient "open-cluster-management.io/api/client/work/clientset/versioned/fake"
|
||||
workinformers "open-cluster-management.io/api/client/work/informers/externalversions"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
helpertest "open-cluster-management.io/work/pkg/hub/test"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPlaceMWControllerPatchStatus(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
pmwTest.Status.PlacedManifestWorkSummary.Total = 1
|
||||
mw, _ := CreateManifestWork(pmwTest, "cls1")
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest, mw)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Second)
|
||||
|
||||
if err := workInformerFactory.Work().V1().ManifestWorks().Informer().GetStore().Add(mw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetStore().Add(pmwTest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
placement, placementDecision := helpertest.CreateTestPlacement("place-test", "default", "cls1")
|
||||
|
||||
fClusterClient := fakeclusterclient.NewSimpleClientset(placement, placementDecision)
|
||||
clusterInformerFactory := clusterinformers.NewSharedInformerFactoryWithOptions(fClusterClient, 1*time.Second)
|
||||
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().Placements().Informer().GetStore().Add(placement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Informer().GetStore().Add(placementDecision); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
placementLister := clusterInformerFactory.Cluster().V1beta1().Placements().Lister()
|
||||
placementDecisionLister := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Lister()
|
||||
|
||||
pmwController := &PlaceManifestWorkController{
|
||||
workClient: fWorkClient,
|
||||
placeManifestWorkLister: workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Lister(),
|
||||
placeManifestWorkIndexer: workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetIndexer(),
|
||||
|
||||
reconcilers: []placeManifestWorkReconcile{
|
||||
&finalizeReconciler{workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
workClient: fWorkClient, manifestWorkLister: mwLister},
|
||||
&addFinalizerReconciler{workClient: fWorkClient},
|
||||
&deployReconciler{workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
manifestWorkLister: mwLister, placementLister: placementLister, placeDecisionLister: placementDecisionLister},
|
||||
&statusReconciler{manifestWorkLister: mwLister},
|
||||
},
|
||||
}
|
||||
|
||||
// create new pmwTestNew with status
|
||||
pmwTestNew := helpertest.CreateTestPlaceManifestWork("pmw-test-new", "default", "place-test-new")
|
||||
pmwTestNew.Status.PlacedManifestWorkSummary.Total = pmwTest.Status.PlacedManifestWorkSummary.Total + 3
|
||||
|
||||
err := pmwController.patchPlaceManifestStatus(context.TODO(), pmwTest, pmwTestNew)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check kubeClient has a patch action to apply
|
||||
actions := ([]clienttesting.Action)(fWorkClient.Actions())
|
||||
if len(actions) == 0 {
|
||||
t.Fatal("fWorkClient Should have patch action ")
|
||||
}
|
||||
// Check placeMW patch name
|
||||
if pmwTest.Name != actions[0].(clienttesting.PatchAction).GetName() {
|
||||
t.Fatal("PlaceMW patch action not match ", actions[0])
|
||||
}
|
||||
}
|
||||
+146
-4
@@ -2,21 +2,163 @@ package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
clusterlister "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1"
|
||||
workclientset "open-cluster-management.io/api/client/work/clientset/versioned"
|
||||
worklisterv1 "open-cluster-management.io/api/client/work/listers/work/v1"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
workv1 "open-cluster-management.io/api/work/v1"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
"open-cluster-management.io/work/pkg/helper"
|
||||
)
|
||||
|
||||
// deployReconciler is to manage ManifestWork based on the placement.
|
||||
type deployReconciler struct {
|
||||
workClient workclientset.Interface
|
||||
workApplier *workapplier.WorkApplier
|
||||
manifestWorkLister worklisterv1.ManifestWorkLister
|
||||
placeDecisionLister clusterlister.PlacementDecisionLister
|
||||
placementLister clusterlister.PlacementLister
|
||||
}
|
||||
|
||||
func (d *deployReconciler) reconcile(ctx context.Context, pw *workapiv1alpha1.PlaceManifestWork) (*workapiv1alpha1.PlaceManifestWork, reconcileState, error) {
|
||||
// TODO the main manifestwork create/update/delete logic, we may also need to manage the status condition for whether manifestwork is created here
|
||||
return pw, reconcileContinue, nil
|
||||
// Manifestwork create/update/delete logic.
|
||||
placement, err := d.placementLister.Placements(pw.Namespace).Get(pw.Spec.PlacementRef.Name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetPlacementDecisionVerified(workapiv1alpha1.PlacementDecisionNotFound, ""))
|
||||
}
|
||||
return pw, reconcileContinue, fmt.Errorf("Failed get placement %w", err)
|
||||
}
|
||||
|
||||
req, err := labels.NewRequirement(PlaceManifestWorkControllerNameLabelKey, selection.In, []string{pw.Name})
|
||||
if err != nil {
|
||||
return pw, reconcileContinue, err
|
||||
}
|
||||
|
||||
selector := labels.NewSelector().Add(*req)
|
||||
manifestWorks, err := d.manifestWorkLister.List(selector)
|
||||
if err != nil {
|
||||
return pw, reconcileContinue, err
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
existingClusters := sets.NewString()
|
||||
for _, mw := range manifestWorks {
|
||||
existingClusters.Insert(mw.Namespace)
|
||||
}
|
||||
|
||||
addedClusters, deletedClusters, err := helper.GetClusters(d.placeDecisionLister, placement, existingClusters)
|
||||
if err != nil {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetPlacementDecisionVerified(workapiv1alpha1.NotAsExpected, ""))
|
||||
|
||||
return pw, reconcileContinue, utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// Create manifestWork for added clusters
|
||||
for cls := range addedClusters {
|
||||
mw, err := CreateManifestWork(pw, cls)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = d.workApplier.Apply(ctx, mw)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update manifestWorks in case there are changes at ManifestWork or PlaceManifestWork
|
||||
for cls := range existingClusters {
|
||||
// Delete manifestWork for deleted clusters
|
||||
if deletedClusters.Has(cls) {
|
||||
err = d.workApplier.Delete(ctx, cls, pw.Name)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
mw, err := CreateManifestWork(pw, cls)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = d.workApplier.Apply(ctx, mw)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the PlacedManifestWorkSummary
|
||||
if pw.Status.PlacedManifestWorkSummary == (workapiv1alpha1.PlacedManifestWorkSummary{}) {
|
||||
pw.Status.PlacedManifestWorkSummary = workapiv1alpha1.PlacedManifestWorkSummary{}
|
||||
}
|
||||
total := len(existingClusters) - len(deletedClusters) + len(addedClusters)
|
||||
if total < 0 {
|
||||
total = 0
|
||||
}
|
||||
|
||||
pw.Status.PlacedManifestWorkSummary.Total = total
|
||||
if total == 0 {
|
||||
pw.Status.PlacedManifestWorkSummary.Applied = 0
|
||||
pw.Status.PlacedManifestWorkSummary.Available = 0
|
||||
pw.Status.PlacedManifestWorkSummary.Degraded = 0
|
||||
pw.Status.PlacedManifestWorkSummary.Progressing = 0
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetPlacementDecisionVerified(workapiv1alpha1.PlacementDecisionEmpty, ""))
|
||||
} else {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetPlacementDecisionVerified(workapiv1alpha1.AsExpected, ""))
|
||||
}
|
||||
|
||||
return pw, reconcileContinue, utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// Return only True status if there all clusters have manifests applied as expected
|
||||
func GetManifestworkApplied(reason string, message string) metav1.Condition {
|
||||
if reason == workapiv1alpha1.AsExpected {
|
||||
return getCondition(string(workapiv1alpha1.ManifestworkApplied), reason, message, metav1.ConditionTrue)
|
||||
}
|
||||
|
||||
return getCondition(string(workapiv1alpha1.ManifestworkApplied), reason, message, metav1.ConditionFalse)
|
||||
|
||||
}
|
||||
|
||||
// Return only True status if there are clusters selected
|
||||
func GetPlacementDecisionVerified(reason string, message string) metav1.Condition {
|
||||
if reason == workapiv1alpha1.AsExpected {
|
||||
return getCondition(string(workapiv1alpha1.PlacementDecisionVerified), reason, message, metav1.ConditionTrue)
|
||||
}
|
||||
|
||||
return getCondition(string(workapiv1alpha1.PlacementDecisionVerified), reason, message, metav1.ConditionFalse)
|
||||
}
|
||||
|
||||
func getCondition(conditionType string, reason string, message string, status metav1.ConditionStatus) metav1.Condition {
|
||||
return metav1.Condition{
|
||||
Type: conditionType,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
Status: status,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func CreateManifestWork(pw *workapiv1alpha1.PlaceManifestWork, clusterNS string) (*workv1.ManifestWork, error) {
|
||||
if clusterNS == "" {
|
||||
return nil, fmt.Errorf("Invalid cluster namespace")
|
||||
}
|
||||
|
||||
return &workv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: pw.Name,
|
||||
Namespace: clusterNS,
|
||||
Labels: map[string]string{PlaceManifestWorkControllerNameLabelKey: pw.Name},
|
||||
},
|
||||
Spec: pw.Spec.ManifestWorkTemplate}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
fakeclusterclient "open-cluster-management.io/api/client/cluster/clientset/versioned/fake"
|
||||
clusterinformers "open-cluster-management.io/api/client/cluster/informers/externalversions"
|
||||
fakeworkclient "open-cluster-management.io/api/client/work/clientset/versioned/fake"
|
||||
workinformers "open-cluster-management.io/api/client/work/informers/externalversions"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
helpertest "open-cluster-management.io/work/pkg/hub/test"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDeployReconcileAsExpected(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
mw, _ := CreateManifestWork(pmwTest, "cls1")
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest, mw)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Second)
|
||||
|
||||
if err := workInformerFactory.Work().V1().ManifestWorks().Informer().GetStore().Add(mw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetStore().Add(pmwTest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
|
||||
// Adding cls2 cluster to placement to check manifestwork added
|
||||
placement, placementDecision := helpertest.CreateTestPlacement("place-test", "default", "cls1", "cls2")
|
||||
fClusterClient := fakeclusterclient.NewSimpleClientset(placement, placementDecision)
|
||||
clusterInformerFactory := clusterinformers.NewSharedInformerFactoryWithOptions(fClusterClient, 1*time.Second)
|
||||
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().Placements().Informer().GetStore().Add(placement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Informer().GetStore().Add(placementDecision); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
placementLister := clusterInformerFactory.Cluster().V1beta1().Placements().Lister()
|
||||
placementDecisionLister := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Lister()
|
||||
|
||||
pmwDeployController := deployReconciler{
|
||||
workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
manifestWorkLister: mwLister,
|
||||
placeDecisionLister: placementDecisionLister,
|
||||
placementLister: placementLister,
|
||||
}
|
||||
|
||||
pmwTest, _, err := pmwDeployController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check for the expected PlacedManifestWorkSummary
|
||||
pmwSummery := workapiv1alpha1.PlacedManifestWorkSummary{
|
||||
Total: int(placement.Status.NumberOfSelectedClusters),
|
||||
Applied: 0,
|
||||
Available: 0,
|
||||
Degraded: 0,
|
||||
Progressing: 0,
|
||||
}
|
||||
|
||||
if pmwTest.Status.PlacedManifestWorkSummary != pmwSummery {
|
||||
t.Fatal("PlacedManifestWorkSummary not as expected ", pmwTest.Status.PlacedManifestWorkSummary, pmwSummery)
|
||||
}
|
||||
|
||||
// Check the PlacedManifestWork conditions
|
||||
placeCondition := apimeta.FindStatusCondition(pmwTest.Status.Conditions, string(workapiv1alpha1.PlacementDecisionVerified))
|
||||
|
||||
if placeCondition == nil {
|
||||
t.Fatal("Placement condition not found ", pmwTest.Status.Conditions)
|
||||
}
|
||||
|
||||
// Check placement condition status true
|
||||
if placeCondition.Status == metav1.ConditionFalse {
|
||||
t.Fatal("Placement condition not True ", placeCondition)
|
||||
}
|
||||
|
||||
// Check placement condition reason
|
||||
if placeCondition.Reason != workapiv1alpha1.AsExpected {
|
||||
t.Fatal("Placement condition Reason not match AsExpected ", placeCondition)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployReconcileAsPlacementDecisionEmpty(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Minute)
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
|
||||
// No clusters added to the placement
|
||||
placement, placementDecision := helpertest.CreateTestPlacement("place-test", "default")
|
||||
fClusterClient := fakeclusterclient.NewSimpleClientset(placement, placementDecision)
|
||||
clusterInformerFactory := clusterinformers.NewSharedInformerFactoryWithOptions(fClusterClient, 1*time.Minute)
|
||||
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().Placements().Informer().GetStore().Add(placement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Informer().GetStore().Add(placementDecision); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
placementLister := clusterInformerFactory.Cluster().V1beta1().Placements().Lister()
|
||||
placementDecisionLister := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Lister()
|
||||
|
||||
pmwDeployController := deployReconciler{
|
||||
workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
manifestWorkLister: mwLister,
|
||||
placeDecisionLister: placementDecisionLister,
|
||||
placementLister: placementLister,
|
||||
}
|
||||
|
||||
pmwTest, _, err := pmwDeployController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check for the expected PlacedManifestWorkSummary
|
||||
pmwSummery := workapiv1alpha1.PlacedManifestWorkSummary{
|
||||
Total: int(placement.Status.NumberOfSelectedClusters),
|
||||
Applied: 0,
|
||||
Available: 0,
|
||||
Degraded: 0,
|
||||
Progressing: 0,
|
||||
}
|
||||
|
||||
if pmwTest.Status.PlacedManifestWorkSummary != pmwSummery {
|
||||
t.Fatal("PlacedManifestWorkSummary not as expected ", pmwTest.Status.PlacedManifestWorkSummary, pmwSummery)
|
||||
}
|
||||
|
||||
// Check the PlacedManifestWork conditions
|
||||
placeCondition := apimeta.FindStatusCondition(pmwTest.Status.Conditions, string(workapiv1alpha1.PlacementDecisionVerified))
|
||||
|
||||
if placeCondition == nil {
|
||||
t.Fatal("Placement condition not found ", pmwTest.Status.Conditions)
|
||||
}
|
||||
|
||||
// Check placement condition status is false
|
||||
if placeCondition.Status == metav1.ConditionTrue {
|
||||
t.Fatal("Placement condition status not False ", placeCondition)
|
||||
}
|
||||
|
||||
// Check placement condition reason is PlacementDecisionEmpty
|
||||
if placeCondition.Reason != workapiv1alpha1.PlacementDecisionEmpty {
|
||||
t.Fatal("Placement condition Reason not match PlacementDecisionEmpty ", placeCondition)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployReconcileAsPlacementNotExist(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-notexist")
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Minute)
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
|
||||
placement, _ := helpertest.CreateTestPlacement("place-test", "default")
|
||||
fClusterClient := fakeclusterclient.NewSimpleClientset(placement)
|
||||
clusterInformerFactory := clusterinformers.NewSharedInformerFactoryWithOptions(fClusterClient, 1*time.Minute)
|
||||
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().Placements().Informer().GetStore().Add(placement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
placementLister := clusterInformerFactory.Cluster().V1beta1().Placements().Lister()
|
||||
placementDecisionLister := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Lister()
|
||||
|
||||
pmwDeployController := deployReconciler{
|
||||
workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
manifestWorkLister: mwLister,
|
||||
placeDecisionLister: placementDecisionLister,
|
||||
placementLister: placementLister,
|
||||
}
|
||||
|
||||
pmwTest, _, err := pmwDeployController.reconcile(context.TODO(), pmwTest)
|
||||
if err == nil {
|
||||
t.Fatal("Expected Not Found Error ", err)
|
||||
}
|
||||
|
||||
// Check the PlacedManifestWork conditions
|
||||
placeCondition := apimeta.FindStatusCondition(pmwTest.Status.Conditions, string(workapiv1alpha1.PlacementDecisionVerified))
|
||||
|
||||
if placeCondition == nil {
|
||||
t.Fatal("Placement condition not found ", pmwTest.Status.Conditions)
|
||||
}
|
||||
|
||||
// Check placement condition status is false
|
||||
if placeCondition.Status == metav1.ConditionTrue {
|
||||
t.Fatal("Placement condition status not False ", placeCondition)
|
||||
}
|
||||
|
||||
// Check placement condition reason is PlacementDecisionNotFound
|
||||
if placeCondition.Reason != workapiv1alpha1.PlacementDecisionNotFound {
|
||||
t.Fatal("Placement condition Reason not match PlacementDecisionEmpty ", placeCondition)
|
||||
}
|
||||
}
|
||||
+37
-5
@@ -2,13 +2,20 @@ package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
workclientset "open-cluster-management.io/api/client/work/clientset/versioned"
|
||||
worklisterv1 "open-cluster-management.io/api/client/work/listers/work/v1"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
"open-cluster-management.io/work/pkg/helper"
|
||||
)
|
||||
|
||||
// finalizeReconciler is to finalize the placeManifestWork by deleteing all related manifestWorks.
|
||||
// finalizeReconciler is to finalize the placeManifestWork by deleting all related manifestWorks.
|
||||
type finalizeReconciler struct {
|
||||
workApplier *workapplier.WorkApplier
|
||||
workClient workclientset.Interface
|
||||
manifestWorkLister worklisterv1.ManifestWorkLister
|
||||
}
|
||||
@@ -31,14 +38,39 @@ func (f *finalizeReconciler) reconcile(ctx context.Context, pw *workapiv1alpha1.
|
||||
}
|
||||
|
||||
if err := f.finalizePlaceManifestWork(ctx, pw); err != nil {
|
||||
return pw, reconcileStop, err
|
||||
return pw, reconcileContinue, err
|
||||
}
|
||||
|
||||
// Remove finalizer after delete all created Manifestworks
|
||||
if helper.RemoveFinalizer(pw, PlaceManifestWorkFinalizer) {
|
||||
_, err := f.workClient.WorkV1alpha1().PlaceManifestWorks(pw.Namespace).Update(ctx, pw, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return pw, reconcileContinue, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove finalizer finally
|
||||
return pw, reconcileStop, nil
|
||||
}
|
||||
|
||||
func (m *finalizeReconciler) finalizePlaceManifestWork(ctx context.Context, placeManifestWork *workapiv1alpha1.PlaceManifestWork) error {
|
||||
// TODO: Delete all ManifestWork owned by the given placeManifestWork
|
||||
return nil
|
||||
req, err := labels.NewRequirement(PlaceManifestWorkControllerNameLabelKey, selection.In, []string{placeManifestWork.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := labels.NewSelector().Add(*req)
|
||||
manifestWorks, err := m.manifestWorkLister.List(selector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
for _, mw := range manifestWorks {
|
||||
err = m.workApplier.Delete(ctx, mw.Namespace, mw.Name)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"golang.org/x/exp/slices"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
fakeclient "open-cluster-management.io/api/client/work/clientset/versioned/fake"
|
||||
workinformers "open-cluster-management.io/api/client/work/informers/externalversions"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
helpertest "open-cluster-management.io/work/pkg/hub/test"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test finalize reconcile
|
||||
func TestFinalizeReconcile(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
mw, _ := CreateManifestWork(pmwTest, "cluster1")
|
||||
fakeClient := fakeclient.NewSimpleClientset(pmwTest, mw)
|
||||
manifestWorkInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fakeClient, 1*time.Second)
|
||||
mwLister := manifestWorkInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
|
||||
finalizerController := finalizeReconciler{
|
||||
workClient: fakeClient,
|
||||
manifestWorkLister: mwLister,
|
||||
workApplier: workapplier.NewWorkApplierWithTypedClient(fakeClient, mwLister),
|
||||
}
|
||||
|
||||
// Set placeManifestWork delete time AND Set finalizer
|
||||
timeNow := metav1.Now()
|
||||
pmwTest.DeletionTimestamp = &timeNow
|
||||
pmwTest.Finalizers = append(pmwTest.Finalizers, PlaceManifestWorkFinalizer)
|
||||
|
||||
pmwTest, _, err := finalizerController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check pmwTest finalizer removed
|
||||
if slices.Contains(pmwTest.Finalizers, PlaceManifestWorkFinalizer) {
|
||||
t.Fatal("Finalizer not deleted", pmwTest.Finalizers)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
fakeclusterclient "open-cluster-management.io/api/client/cluster/clientset/versioned/fake"
|
||||
clusterinformers "open-cluster-management.io/api/client/cluster/informers/externalversions"
|
||||
fakeworkclient "open-cluster-management.io/api/client/work/clientset/versioned/fake"
|
||||
workinformers "open-cluster-management.io/api/client/work/informers/externalversions"
|
||||
"open-cluster-management.io/api/utils/work/v1/workapplier"
|
||||
helpertest "open-cluster-management.io/work/pkg/hub/test"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPlaceMWControllerIndex(t *testing.T) {
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
pmwTest.Status.PlacedManifestWorkSummary.Total = 1
|
||||
mw, _ := CreateManifestWork(pmwTest, "cls1")
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest, mw)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Second)
|
||||
|
||||
err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().AddIndexers(
|
||||
cache.Indexers{placeManifestWorkByPlacement: indexPlacementManifestWorkByPlacement})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := workInformerFactory.Work().V1().ManifestWorks().Informer().GetStore().Add(mw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetStore().Add(pmwTest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
placement, placementDecision := helpertest.CreateTestPlacement("place-test", "default", "cls1")
|
||||
|
||||
fClusterClient := fakeclusterclient.NewSimpleClientset(placement, placementDecision)
|
||||
clusterInformerFactory := clusterinformers.NewSharedInformerFactoryWithOptions(fClusterClient, 1*time.Second)
|
||||
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().Placements().Informer().GetStore().Add(placement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Informer().GetStore().Add(placementDecision); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
placementLister := clusterInformerFactory.Cluster().V1beta1().Placements().Lister()
|
||||
placementDecisionLister := clusterInformerFactory.Cluster().V1beta1().PlacementDecisions().Lister()
|
||||
|
||||
pmwController := &PlaceManifestWorkController{
|
||||
workClient: fWorkClient,
|
||||
placeManifestWorkLister: workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Lister(),
|
||||
placeManifestWorkIndexer: workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetIndexer(),
|
||||
|
||||
reconcilers: []placeManifestWorkReconcile{
|
||||
&finalizeReconciler{workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
workClient: fWorkClient, manifestWorkLister: mwLister},
|
||||
&addFinalizerReconciler{workClient: fWorkClient},
|
||||
&deployReconciler{workApplier: workapplier.NewWorkApplierWithTypedClient(fWorkClient, mwLister),
|
||||
manifestWorkLister: mwLister, placementLister: placementLister, placeDecisionLister: placementDecisionLister},
|
||||
&statusReconciler{manifestWorkLister: mwLister},
|
||||
},
|
||||
}
|
||||
|
||||
// Check index key creation
|
||||
placementKey, err := indexPlacementManifestWorkByPlacement(pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(placementKey) == 0 {
|
||||
t.Fatal("Key not created", placementKey)
|
||||
}
|
||||
if placementKey[0] != pmwTest.Namespace+"/"+pmwTest.Spec.PlacementRef.Name {
|
||||
t.Fatal("placement Key not match ", placementKey[0])
|
||||
}
|
||||
|
||||
expectedKey := pmwTest.Namespace + "/" + pmwTest.Name
|
||||
placeNotExist, placeDecisinNotExist := helpertest.CreateTestPlacement("place-notExist", "ns-notExist")
|
||||
// Check placement Queue Keys
|
||||
keys := pmwController.placementQueueKeysFunc(placement)
|
||||
if len(keys) == 0 {
|
||||
t.Fatal("placement index keys not exist")
|
||||
}
|
||||
if keys[0] != expectedKey {
|
||||
t.Fatal("Expected placement key not match ", keys[0], " - ", expectedKey)
|
||||
}
|
||||
// Check placement Queue Keys not exist
|
||||
keys = pmwController.placementQueueKeysFunc(placeNotExist)
|
||||
if len(keys) > 0 {
|
||||
t.Fatal("placement index keys should not exist ", keys)
|
||||
}
|
||||
|
||||
// Check placementDecision Queue Keys
|
||||
keys = pmwController.placementDecisionQueueKeysFunc(placementDecision)
|
||||
if len(keys) == 0 {
|
||||
t.Fatal("placement decision index keys not exist")
|
||||
}
|
||||
if keys[0] != expectedKey {
|
||||
t.Fatal("Expected placementDecision key not match ", keys[0], " - ", expectedKey)
|
||||
}
|
||||
// Check placementDecision Queue Keys not exist
|
||||
keys = pmwController.placementDecisionQueueKeysFunc(placeDecisinNotExist)
|
||||
if len(keys) > 0 {
|
||||
t.Fatal("placement decision index keys should not exist ", keys)
|
||||
}
|
||||
|
||||
// Check manifestWork Queue Keys
|
||||
key := pmwController.manifestWorkQueueKeyFunc(mw)
|
||||
if key != pmwTest.Name {
|
||||
t.Fatal("Expected manifestwork key not match", key, " - ", pmwTest.Name)
|
||||
}
|
||||
// Check manifestWork Queue Keys not exist
|
||||
mw.Labels = map[string]string{"testLabel": "label1"}
|
||||
key = pmwController.manifestWorkQueueKeyFunc(mw)
|
||||
if key != "" {
|
||||
t.Fatal("Expected manifestwork key should not exist ", key)
|
||||
}
|
||||
}
|
||||
+71
-6
@@ -2,20 +2,85 @@ package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
clusterlister "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1"
|
||||
workclientset "open-cluster-management.io/api/client/work/clientset/versioned"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
worklisterv1 "open-cluster-management.io/api/client/work/listers/work/v1"
|
||||
workapiv1 "open-cluster-management.io/api/work/v1"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
)
|
||||
|
||||
// statusReconciler is to update placeManifestwork status.
|
||||
type statusReconciler struct {
|
||||
workClient workclientset.Interface
|
||||
manifestWorkLister worklisterv1.ManifestWorkLister
|
||||
placeDecisionLister clusterlister.PlacementDecisionLister
|
||||
manifestWorkLister worklisterv1.ManifestWorkLister
|
||||
}
|
||||
|
||||
func (d *statusReconciler) reconcile(ctx context.Context, pw *workapiv1alpha1.PlaceManifestWork) (*workapiv1alpha1.PlaceManifestWork, reconcileState, error) {
|
||||
// TODO the logic for update placeManifestwork status
|
||||
// The logic for update placeManifestwork status
|
||||
if pw.Status.PlacedManifestWorkSummary.Total == 0 {
|
||||
condition := apimeta.FindStatusCondition(pw.Status.Conditions, string(workapiv1alpha1.PlacementDecisionVerified))
|
||||
if condition != nil && condition.Reason == workapiv1alpha1.PlacementDecisionEmpty {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetManifestworkApplied(workapiv1alpha1.PlacementDecisionEmpty, ""))
|
||||
} else {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetManifestworkApplied(workapiv1alpha1.NotAsExpected, ""))
|
||||
}
|
||||
|
||||
return pw, reconcileContinue, nil
|
||||
}
|
||||
|
||||
req, err := labels.NewRequirement(PlaceManifestWorkControllerNameLabelKey, selection.In, []string{pw.Name})
|
||||
if err != nil {
|
||||
return pw, reconcileContinue, err
|
||||
}
|
||||
|
||||
selector := labels.NewSelector().Add(*req)
|
||||
manifestWorks, err := d.manifestWorkLister.List(selector)
|
||||
if err != nil {
|
||||
return pw, reconcileContinue, err
|
||||
}
|
||||
|
||||
appliedCount, availableCount, degradCount, processingCount := 0, 0, 0, 0
|
||||
for _, mw := range manifestWorks {
|
||||
if !mw.DeletionTimestamp.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
// applied condition
|
||||
condition := apimeta.FindStatusCondition(mw.Status.Conditions, string(workapiv1.ManifestApplied))
|
||||
if condition != nil && condition.Status == metav1.ConditionTrue {
|
||||
appliedCount++
|
||||
}
|
||||
// Progressing condition
|
||||
condition = apimeta.FindStatusCondition(mw.Status.Conditions, string(workapiv1.ManifestProgressing))
|
||||
if condition != nil && condition.Status == metav1.ConditionTrue {
|
||||
processingCount++
|
||||
}
|
||||
// Available condition
|
||||
condition = apimeta.FindStatusCondition(mw.Status.Conditions, string(workapiv1.ManifestAvailable))
|
||||
if condition != nil && condition.Status == metav1.ConditionTrue {
|
||||
availableCount++
|
||||
}
|
||||
// Degraded condition
|
||||
condition = apimeta.FindStatusCondition(mw.Status.Conditions, string(workapiv1.ManifestDegraded))
|
||||
if condition != nil && condition.Status == metav1.ConditionTrue {
|
||||
degradCount++
|
||||
}
|
||||
}
|
||||
|
||||
pw.Status.PlacedManifestWorkSummary.Available = availableCount
|
||||
pw.Status.PlacedManifestWorkSummary.Degraded = degradCount
|
||||
pw.Status.PlacedManifestWorkSummary.Progressing = processingCount
|
||||
pw.Status.PlacedManifestWorkSummary.Applied = appliedCount
|
||||
|
||||
if pw.Status.PlacedManifestWorkSummary.Available == pw.Status.PlacedManifestWorkSummary.Total &&
|
||||
pw.Status.PlacedManifestWorkSummary.Progressing == 0 && pw.Status.PlacedManifestWorkSummary.Degraded == 0 {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetManifestworkApplied(workapiv1alpha1.AsExpected, ""))
|
||||
} else if pw.Status.PlacedManifestWorkSummary.Progressing > 0 && pw.Status.PlacedManifestWorkSummary.Degraded == 0 {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetManifestworkApplied(workapiv1alpha1.Processing, ""))
|
||||
} else {
|
||||
apimeta.SetStatusCondition(&pw.Status.Conditions, GetManifestworkApplied(workapiv1alpha1.NotAsExpected, ""))
|
||||
}
|
||||
|
||||
return pw, reconcileContinue, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
package placemanifestworkcontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
fakeworkclient "open-cluster-management.io/api/client/work/clientset/versioned/fake"
|
||||
workinformers "open-cluster-management.io/api/client/work/informers/externalversions"
|
||||
workv1 "open-cluster-management.io/api/work/v1"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
helpertest "open-cluster-management.io/work/pkg/hub/test"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStatusReconcileAsExpected(t *testing.T) {
|
||||
clusters := []string{"cls1", "cls2", "cls3", "cls4"}
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
pmwTest.Status.PlacedManifestWorkSummary.Total = len(clusters)
|
||||
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Second)
|
||||
|
||||
if err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetStore().Add(pmwTest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, cls := range clusters {
|
||||
mw, _ := CreateManifestWork(pmwTest, cls)
|
||||
cond := getCondition(string(workv1.ManifestApplied), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
|
||||
cond = getCondition(string(workv1.ManifestAvailable), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
if err := workInformerFactory.Work().V1().ManifestWorks().Informer().GetStore().Add(mw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
pmwStatusController := statusReconciler{
|
||||
manifestWorkLister: mwLister,
|
||||
}
|
||||
|
||||
pmwTest, _, err := pmwStatusController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check for the expected PlacedManifestWorkSummary
|
||||
pmwSummery := workapiv1alpha1.PlacedManifestWorkSummary{
|
||||
Total: len(clusters),
|
||||
Applied: len(clusters),
|
||||
Available: len(clusters),
|
||||
Degraded: 0,
|
||||
Progressing: 0,
|
||||
}
|
||||
|
||||
if pmwTest.Status.PlacedManifestWorkSummary != pmwSummery {
|
||||
t.Fatal("PlacedManifestWorkSummary not as expected ", pmwTest.Status.PlacedManifestWorkSummary, pmwSummery)
|
||||
}
|
||||
|
||||
// Check the ManifestworkApplied conditions
|
||||
appliedCondition := apimeta.FindStatusCondition(pmwTest.Status.Conditions, string(workapiv1alpha1.ManifestworkApplied))
|
||||
|
||||
if appliedCondition == nil {
|
||||
t.Fatal("Applied condition not found ", pmwTest.Status.Conditions)
|
||||
}
|
||||
|
||||
// Check ManifestworkApplied condition status true
|
||||
if appliedCondition.Status == metav1.ConditionFalse {
|
||||
t.Fatal("Applied condition not True ", appliedCondition)
|
||||
}
|
||||
|
||||
// Check ManifestworkApplied condition reason
|
||||
if appliedCondition.Reason != workapiv1alpha1.AsExpected {
|
||||
t.Fatal("Applied condition Reason not match AsExpected ", appliedCondition)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusReconcileAsProcessing(t *testing.T) {
|
||||
clusters := []string{"cls1", "cls2", "cls3", "cls4"}
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
pmwTest.Status.PlacedManifestWorkSummary.Total = len(clusters)
|
||||
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Second)
|
||||
|
||||
if err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetStore().Add(pmwTest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for id, cls := range clusters {
|
||||
mw, _ := CreateManifestWork(pmwTest, cls)
|
||||
cond := getCondition(string(workv1.ManifestApplied), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
|
||||
if id%2 == 0 {
|
||||
cond = getCondition(string(workv1.ManifestAvailable), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
} else {
|
||||
cond = getCondition(string(workv1.ManifestProgressing), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
}
|
||||
|
||||
if err := workInformerFactory.Work().V1().ManifestWorks().Informer().GetStore().Add(mw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
pmwStatusController := statusReconciler{
|
||||
manifestWorkLister: mwLister,
|
||||
}
|
||||
|
||||
pmwTest, _, err := pmwStatusController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check for the expected PlacedManifestWorkSummary
|
||||
pmwSummery := workapiv1alpha1.PlacedManifestWorkSummary{
|
||||
Total: len(clusters),
|
||||
Applied: len(clusters),
|
||||
Available: len(clusters) / 2,
|
||||
Degraded: 0,
|
||||
Progressing: len(clusters) / 2,
|
||||
}
|
||||
|
||||
if pmwTest.Status.PlacedManifestWorkSummary != pmwSummery {
|
||||
t.Fatal("PlacedManifestWorkSummary not as expected ", pmwTest.Status.PlacedManifestWorkSummary, pmwSummery)
|
||||
}
|
||||
|
||||
// Check the ManifestworkApplied conditions
|
||||
appliedCondition := apimeta.FindStatusCondition(pmwTest.Status.Conditions, string(workapiv1alpha1.ManifestworkApplied))
|
||||
|
||||
if appliedCondition == nil {
|
||||
t.Fatal("Applied condition not found ", pmwTest.Status.Conditions)
|
||||
}
|
||||
|
||||
// Check ManifestworkApplied condition status false
|
||||
if appliedCondition.Status == metav1.ConditionTrue {
|
||||
t.Fatal("Applied condition not True ", appliedCondition)
|
||||
}
|
||||
|
||||
// Check ManifestworkApplied condition reason
|
||||
if appliedCondition.Reason != workapiv1alpha1.Processing {
|
||||
t.Fatal("Applied condition Reason not match Processing ", appliedCondition)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusReconcileNotAsExpected(t *testing.T) {
|
||||
clusters := []string{"cls1", "cls2", "cls3", "cls4"}
|
||||
pmwTest := helpertest.CreateTestPlaceManifestWork("pmw-test", "default", "place-test")
|
||||
pmwTest.Status.PlacedManifestWorkSummary.Total = len(clusters)
|
||||
|
||||
fWorkClient := fakeworkclient.NewSimpleClientset(pmwTest)
|
||||
workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(fWorkClient, 1*time.Second)
|
||||
|
||||
if err := workInformerFactory.Work().V1alpha1().PlaceManifestWorks().Informer().GetStore().Add(pmwTest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
avaCount, processingCount, degradCount := 0, 0, 0
|
||||
for id, cls := range clusters {
|
||||
mw, _ := CreateManifestWork(pmwTest, cls)
|
||||
cond := getCondition(string(workv1.ManifestApplied), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
|
||||
if id%2 == 0 {
|
||||
cond = getCondition(string(workv1.ManifestAvailable), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
avaCount++
|
||||
} else if id%3 == 0 {
|
||||
cond = getCondition(string(workv1.ManifestDegraded), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
processingCount++
|
||||
} else {
|
||||
cond = getCondition(string(workv1.ManifestProgressing), "", "", metav1.ConditionTrue)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, cond)
|
||||
degradCount++
|
||||
}
|
||||
|
||||
if err := workInformerFactory.Work().V1().ManifestWorks().Informer().GetStore().Add(mw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
mwLister := workInformerFactory.Work().V1().ManifestWorks().Lister()
|
||||
pmwStatusController := statusReconciler{
|
||||
manifestWorkLister: mwLister,
|
||||
}
|
||||
|
||||
pmwTest, _, err := pmwStatusController.reconcile(context.TODO(), pmwTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check for the expected PlacedManifestWorkSummary
|
||||
pmwSummery := workapiv1alpha1.PlacedManifestWorkSummary{
|
||||
Total: len(clusters),
|
||||
Applied: len(clusters),
|
||||
Available: avaCount,
|
||||
Degraded: degradCount,
|
||||
Progressing: processingCount,
|
||||
}
|
||||
|
||||
if pmwTest.Status.PlacedManifestWorkSummary != pmwSummery {
|
||||
t.Fatal("PlacedManifestWorkSummary not as expected ", pmwTest.Status.PlacedManifestWorkSummary, pmwSummery)
|
||||
}
|
||||
|
||||
// Check the ManifestworkApplied conditions
|
||||
appliedCondition := apimeta.FindStatusCondition(pmwTest.Status.Conditions, string(workapiv1alpha1.ManifestworkApplied))
|
||||
|
||||
if appliedCondition == nil {
|
||||
t.Fatal("Applied condition not found ", pmwTest.Status.Conditions)
|
||||
}
|
||||
|
||||
// Check ManifestworkApplied condition status false
|
||||
if appliedCondition.Status == metav1.ConditionTrue {
|
||||
t.Fatal("Applied condition not True ", appliedCondition)
|
||||
}
|
||||
|
||||
// Check ManifestworkApplied condition reason
|
||||
if appliedCondition.Reason != workapiv1alpha1.NotAsExpected {
|
||||
t.Fatal("Applied condition Reason not match NotAsExpected ", appliedCondition)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
workapiv1 "open-cluster-management.io/api/work/v1"
|
||||
workapiv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
|
||||
)
|
||||
|
||||
func CreateTestPlaceManifestWork(name string, ns string, placementName string) *workapiv1alpha1.PlaceManifestWork {
|
||||
pmw := &workapiv1alpha1.PlaceManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: ns,
|
||||
ResourceVersion: "316679655",
|
||||
UID: "0b1441ec-717f-4877-a165-27e5b59245f5",
|
||||
},
|
||||
Spec: workapiv1alpha1.PlaceManifestWorkSpec{
|
||||
ManifestWorkTemplate: workapiv1.ManifestWorkSpec{
|
||||
Workload: workapiv1.ManifestsTemplate{
|
||||
Manifests: []workapiv1.Manifest{},
|
||||
},
|
||||
},
|
||||
PlacementRef: workapiv1alpha1.LocalPlacementReference{
|
||||
Name: placementName,
|
||||
},
|
||||
},
|
||||
}
|
||||
return pmw
|
||||
}
|
||||
|
||||
// Return placement with predicate of label cluster name
|
||||
func CreateTestPlacement(name string, ns string, clusters ...string) (*clusterv1beta1.Placement, *clusterv1beta1.PlacementDecision) {
|
||||
namereq := metav1.LabelSelectorRequirement{}
|
||||
namereq.Key = "name"
|
||||
namereq.Operator = metav1.LabelSelectorOpIn
|
||||
for _, cls := range clusters {
|
||||
namereq.Values = append(namereq.Values, cls)
|
||||
}
|
||||
|
||||
labelSelector := &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{namereq},
|
||||
}
|
||||
|
||||
clusterPredicate := clusterv1beta1.ClusterPredicate{
|
||||
RequiredClusterSelector: clusterv1beta1.ClusterSelector{
|
||||
LabelSelector: *labelSelector,
|
||||
},
|
||||
}
|
||||
|
||||
placement := &clusterv1beta1.Placement{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
Spec: clusterv1beta1.PlacementSpec{
|
||||
Predicates: []clusterv1beta1.ClusterPredicate{clusterPredicate},
|
||||
},
|
||||
}
|
||||
placement.Status.NumberOfSelectedClusters = int32(len(clusters))
|
||||
|
||||
placementDecision := &clusterv1beta1.PlacementDecision{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name + "-decision",
|
||||
Namespace: ns,
|
||||
Labels: map[string]string{clusterv1beta1.PlacementLabel: name},
|
||||
},
|
||||
}
|
||||
|
||||
decisions := []clusterv1beta1.ClusterDecision{}
|
||||
for _, cls := range clusters {
|
||||
decisions = append(decisions, clusterv1beta1.ClusterDecision{
|
||||
ClusterName: cls,
|
||||
})
|
||||
}
|
||||
placementDecision.Status.Decisions = decisions
|
||||
|
||||
return placement, placementDecision
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package constraints defines a set of useful constraints to be used
|
||||
// with type parameters.
|
||||
package constraints
|
||||
|
||||
// Signed is a constraint that permits any signed integer type.
|
||||
// If future releases of Go add new predeclared signed integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
// Unsigned is a constraint that permits any unsigned integer type.
|
||||
// If future releases of Go add new predeclared unsigned integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// Integer is a constraint that permits any integer type.
|
||||
// If future releases of Go add new predeclared integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Integer interface {
|
||||
Signed | Unsigned
|
||||
}
|
||||
|
||||
// Float is a constraint that permits any floating-point type.
|
||||
// If future releases of Go add new predeclared floating-point types,
|
||||
// this constraint will be modified to include them.
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
// Complex is a constraint that permits any complex numeric type.
|
||||
// If future releases of Go add new predeclared complex numeric types,
|
||||
// this constraint will be modified to include them.
|
||||
type Complex interface {
|
||||
~complex64 | ~complex128
|
||||
}
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
type Ordered interface {
|
||||
Integer | Float | ~string
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package slices defines various functions useful with slices of any type.
|
||||
// Unless otherwise specified, these functions all apply to the elements
|
||||
// of a slice at index 0 <= i < len(s).
|
||||
//
|
||||
// Note that the less function in IsSortedFunc, SortFunc, SortStableFunc requires a
|
||||
// strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings),
|
||||
// or the sorting may fail to sort correctly. A common case is when sorting slices of
|
||||
// floating-point numbers containing NaN values.
|
||||
package slices
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
|
||||
// Equal reports whether two slices are equal: the same length and all
|
||||
// elements equal. If the lengths are different, Equal returns false.
|
||||
// Otherwise, the elements are compared in increasing index order, and the
|
||||
// comparison stops at the first unequal pair.
|
||||
// Floating point NaNs are not considered equal.
|
||||
func Equal[E comparable](s1, s2 []E) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i := range s1 {
|
||||
if s1[i] != s2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualFunc reports whether two slices are equal using a comparison
|
||||
// function on each pair of elements. If the lengths are different,
|
||||
// EqualFunc returns false. Otherwise, the elements are compared in
|
||||
// increasing index order, and the comparison stops at the first index
|
||||
// for which eq returns false.
|
||||
func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range s1 {
|
||||
v2 := s2[i]
|
||||
if !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Compare compares the elements of s1 and s2.
|
||||
// The elements are compared sequentially, starting at index 0,
|
||||
// until one element is not equal to the other.
|
||||
// The result of comparing the first non-matching elements is returned.
|
||||
// If both slices are equal until one of them ends, the shorter slice is
|
||||
// considered less than the longer one.
|
||||
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
|
||||
// Comparisons involving floating point NaNs are ignored.
|
||||
func Compare[E constraints.Ordered](s1, s2 []E) int {
|
||||
s2len := len(s2)
|
||||
for i, v1 := range s1 {
|
||||
if i >= s2len {
|
||||
return +1
|
||||
}
|
||||
v2 := s2[i]
|
||||
switch {
|
||||
case v1 < v2:
|
||||
return -1
|
||||
case v1 > v2:
|
||||
return +1
|
||||
}
|
||||
}
|
||||
if len(s1) < s2len {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CompareFunc is like Compare but uses a comparison function
|
||||
// on each pair of elements. The elements are compared in increasing
|
||||
// index order, and the comparisons stop after the first time cmp
|
||||
// returns non-zero.
|
||||
// The result is the first non-zero result of cmp; if cmp always
|
||||
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
|
||||
// and +1 if len(s1) > len(s2).
|
||||
func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
|
||||
s2len := len(s2)
|
||||
for i, v1 := range s1 {
|
||||
if i >= s2len {
|
||||
return +1
|
||||
}
|
||||
v2 := s2[i]
|
||||
if c := cmp(v1, v2); c != 0 {
|
||||
return c
|
||||
}
|
||||
}
|
||||
if len(s1) < s2len {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Index returns the index of the first occurrence of v in s,
|
||||
// or -1 if not present.
|
||||
func Index[E comparable](s []E, v E) int {
|
||||
for i, vs := range s {
|
||||
if v == vs {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// IndexFunc returns the first index i satisfying f(s[i]),
|
||||
// or -1 if none do.
|
||||
func IndexFunc[E any](s []E, f func(E) bool) int {
|
||||
for i, v := range s {
|
||||
if f(v) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Contains reports whether v is present in s.
|
||||
func Contains[E comparable](s []E, v E) bool {
|
||||
return Index(s, v) >= 0
|
||||
}
|
||||
|
||||
// ContainsFunc reports whether at least one
|
||||
// element e of s satisfies f(e).
|
||||
func ContainsFunc[E any](s []E, f func(E) bool) bool {
|
||||
return IndexFunc(s, f) >= 0
|
||||
}
|
||||
|
||||
// Insert inserts the values v... into s at index i,
|
||||
// returning the modified slice.
|
||||
// In the returned slice r, r[i] == v[0].
|
||||
// Insert panics if i is out of range.
|
||||
// This function is O(len(s) + len(v)).
|
||||
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
|
||||
tot := len(s) + len(v)
|
||||
if tot <= cap(s) {
|
||||
s2 := s[:tot]
|
||||
copy(s2[i+len(v):], s[i:])
|
||||
copy(s2[i:], v)
|
||||
return s2
|
||||
}
|
||||
s2 := make(S, tot)
|
||||
copy(s2, s[:i])
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+len(v):], s[i:])
|
||||
return s2
|
||||
}
|
||||
|
||||
// Delete removes the elements s[i:j] from s, returning the modified slice.
|
||||
// Delete panics if s[i:j] is not a valid slice of s.
|
||||
// Delete modifies the contents of the slice s; it does not create a new slice.
|
||||
// Delete is O(len(s)-j), so if many items must be deleted, it is better to
|
||||
// make a single call deleting them all together than to delete one at a time.
|
||||
// Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those
|
||||
// elements contain pointers you might consider zeroing those elements so that
|
||||
// objects they reference can be garbage collected.
|
||||
func Delete[S ~[]E, E any](s S, i, j int) S {
|
||||
_ = s[i:j] // bounds check
|
||||
|
||||
return append(s[:i], s[j:]...)
|
||||
}
|
||||
|
||||
// Replace replaces the elements s[i:j] by the given v, and returns the
|
||||
// modified slice. Replace panics if s[i:j] is not a valid slice of s.
|
||||
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
||||
_ = s[i:j] // verify that i:j is a valid subslice
|
||||
tot := len(s[:i]) + len(v) + len(s[j:])
|
||||
if tot <= cap(s) {
|
||||
s2 := s[:tot]
|
||||
copy(s2[i+len(v):], s[j:])
|
||||
copy(s2[i:], v)
|
||||
return s2
|
||||
}
|
||||
s2 := make(S, tot)
|
||||
copy(s2, s[:i])
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+len(v):], s[j:])
|
||||
return s2
|
||||
}
|
||||
|
||||
// Clone returns a copy of the slice.
|
||||
// The elements are copied using assignment, so this is a shallow clone.
|
||||
func Clone[S ~[]E, E any](s S) S {
|
||||
// Preserve nil in case it matters.
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return append(S([]E{}), s...)
|
||||
}
|
||||
|
||||
// Compact replaces consecutive runs of equal elements with a single copy.
|
||||
// This is like the uniq command found on Unix.
|
||||
// Compact modifies the contents of the slice s; it does not create a new slice.
|
||||
// When Compact discards m elements in total, it might not modify the elements
|
||||
// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
|
||||
// zeroing those elements so that objects they reference can be garbage collected.
|
||||
func Compact[S ~[]E, E comparable](s S) S {
|
||||
if len(s) < 2 {
|
||||
return s
|
||||
}
|
||||
i := 1
|
||||
last := s[0]
|
||||
for _, v := range s[1:] {
|
||||
if v != last {
|
||||
s[i] = v
|
||||
i++
|
||||
last = v
|
||||
}
|
||||
}
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// CompactFunc is like Compact but uses a comparison function.
|
||||
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
|
||||
if len(s) < 2 {
|
||||
return s
|
||||
}
|
||||
i := 1
|
||||
last := s[0]
|
||||
for _, v := range s[1:] {
|
||||
if !eq(v, last) {
|
||||
s[i] = v
|
||||
i++
|
||||
last = v
|
||||
}
|
||||
}
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// Grow increases the slice's capacity, if necessary, to guarantee space for
|
||||
// another n elements. After Grow(n), at least n elements can be appended
|
||||
// to the slice without another allocation. If n is negative or too large to
|
||||
// allocate the memory, Grow panics.
|
||||
func Grow[S ~[]E, E any](s S, n int) S {
|
||||
if n < 0 {
|
||||
panic("cannot be negative")
|
||||
}
|
||||
if n -= cap(s) - len(s); n > 0 {
|
||||
// TODO(https://go.dev/issue/53888): Make using []E instead of S
|
||||
// to workaround a compiler bug where the runtime.growslice optimization
|
||||
// does not take effect. Revert when the compiler is fixed.
|
||||
s = append([]E(s)[:cap(s)], make([]E, n)...)[:len(s)]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
|
||||
func Clip[S ~[]E, E any](s S) S {
|
||||
return s[:len(s):len(s)]
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package slices
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Sort sorts a slice of any ordered type in ascending order.
|
||||
// Sort may fail to sort correctly when sorting slices of floating-point
|
||||
// numbers containing Not-a-number (NaN) values.
|
||||
// Use slices.SortFunc(x, func(a, b float64) bool {return a < b || (math.IsNaN(a) && !math.IsNaN(b))})
|
||||
// instead if the input may contain NaNs.
|
||||
func Sort[E constraints.Ordered](x []E) {
|
||||
n := len(x)
|
||||
pdqsortOrdered(x, 0, n, bits.Len(uint(n)))
|
||||
}
|
||||
|
||||
// SortFunc sorts the slice x in ascending order as determined by the less function.
|
||||
// This sort is not guaranteed to be stable.
|
||||
//
|
||||
// SortFunc requires that less is a strict weak ordering.
|
||||
// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
|
||||
func SortFunc[E any](x []E, less func(a, b E) bool) {
|
||||
n := len(x)
|
||||
pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less)
|
||||
}
|
||||
|
||||
// SortStableFunc sorts the slice x while keeping the original order of equal
|
||||
// elements, using less to compare elements.
|
||||
func SortStableFunc[E any](x []E, less func(a, b E) bool) {
|
||||
stableLessFunc(x, len(x), less)
|
||||
}
|
||||
|
||||
// IsSorted reports whether x is sorted in ascending order.
|
||||
func IsSorted[E constraints.Ordered](x []E) bool {
|
||||
for i := len(x) - 1; i > 0; i-- {
|
||||
if x[i] < x[i-1] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSortedFunc reports whether x is sorted in ascending order, with less as the
|
||||
// comparison function.
|
||||
func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool {
|
||||
for i := len(x) - 1; i > 0; i-- {
|
||||
if less(x[i], x[i-1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// BinarySearch searches for target in a sorted slice and returns the position
|
||||
// where target is found, or the position where target would appear in the
|
||||
// sort order; it also returns a bool saying whether the target is really found
|
||||
// in the slice. The slice must be sorted in increasing order.
|
||||
func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) {
|
||||
// Inlining is faster than calling BinarySearchFunc with a lambda.
|
||||
n := len(x)
|
||||
// Define x[-1] < target and x[n] >= target.
|
||||
// Invariant: x[i-1] < target, x[j] >= target.
|
||||
i, j := 0, n
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if x[h] < target {
|
||||
i = h + 1 // preserves x[i-1] < target
|
||||
} else {
|
||||
j = h // preserves x[j] >= target
|
||||
}
|
||||
}
|
||||
// i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i.
|
||||
return i, i < n && x[i] == target
|
||||
}
|
||||
|
||||
// BinarySearchFunc works like BinarySearch, but uses a custom comparison
|
||||
// function. The slice must be sorted in increasing order, where "increasing" is
|
||||
// defined by cmp. cmp(a, b) is expected to return an integer comparing the two
|
||||
// parameters: 0 if a == b, a negative number if a < b and a positive number if
|
||||
// a > b.
|
||||
func BinarySearchFunc[E any](x []E, target E, cmp func(E, E) int) (int, bool) {
|
||||
n := len(x)
|
||||
// Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
|
||||
// Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
|
||||
i, j := 0, n
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if cmp(x[h], target) < 0 {
|
||||
i = h + 1 // preserves cmp(x[i - 1], target) < 0
|
||||
} else {
|
||||
j = h // preserves cmp(x[j], target) >= 0
|
||||
}
|
||||
}
|
||||
// i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i.
|
||||
return i, i < n && cmp(x[i], target) == 0
|
||||
}
|
||||
|
||||
type sortedHint int // hint for pdqsort when choosing the pivot
|
||||
|
||||
const (
|
||||
unknownHint sortedHint = iota
|
||||
increasingHint
|
||||
decreasingHint
|
||||
)
|
||||
|
||||
// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
|
||||
type xorshift uint64
|
||||
|
||||
func (r *xorshift) Next() uint64 {
|
||||
*r ^= *r << 13
|
||||
*r ^= *r >> 17
|
||||
*r ^= *r << 5
|
||||
return uint64(*r)
|
||||
}
|
||||
|
||||
func nextPowerOfTwo(length int) uint {
|
||||
return 1 << bits.Len(uint(length))
|
||||
}
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
// Code generated by gen_sort_variants.go; DO NOT EDIT.
|
||||
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package slices
|
||||
|
||||
// insertionSortLessFunc sorts data[a:b] using insertion sort.
|
||||
func insertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
|
||||
for i := a + 1; i < b; i++ {
|
||||
for j := i; j > a && less(data[j], data[j-1]); j-- {
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// siftDownLessFunc implements the heap property on data[lo:hi].
|
||||
// first is an offset into the array where the root of the heap lies.
|
||||
func siftDownLessFunc[E any](data []E, lo, hi, first int, less func(a, b E) bool) {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && less(data[first+child], data[first+child+1]) {
|
||||
child++
|
||||
}
|
||||
if !less(data[first+root], data[first+child]) {
|
||||
return
|
||||
}
|
||||
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func heapSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
|
||||
first := a
|
||||
lo := 0
|
||||
hi := b - a
|
||||
|
||||
// Build heap with greatest element at top.
|
||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||
siftDownLessFunc(data, i, hi, first, less)
|
||||
}
|
||||
|
||||
// Pop elements, largest first, into end of data.
|
||||
for i := hi - 1; i >= 0; i-- {
|
||||
data[first], data[first+i] = data[first+i], data[first]
|
||||
siftDownLessFunc(data, lo, i, first, less)
|
||||
}
|
||||
}
|
||||
|
||||
// pdqsortLessFunc sorts data[a:b].
|
||||
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
||||
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
||||
// C++ implementation: https://github.com/orlp/pdqsort
|
||||
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
||||
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
||||
func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) {
|
||||
const maxInsertion = 12
|
||||
|
||||
var (
|
||||
wasBalanced = true // whether the last partitioning was reasonably balanced
|
||||
wasPartitioned = true // whether the slice was already partitioned
|
||||
)
|
||||
|
||||
for {
|
||||
length := b - a
|
||||
|
||||
if length <= maxInsertion {
|
||||
insertionSortLessFunc(data, a, b, less)
|
||||
return
|
||||
}
|
||||
|
||||
// Fall back to heapsort if too many bad choices were made.
|
||||
if limit == 0 {
|
||||
heapSortLessFunc(data, a, b, less)
|
||||
return
|
||||
}
|
||||
|
||||
// If the last partitioning was imbalanced, we need to breaking patterns.
|
||||
if !wasBalanced {
|
||||
breakPatternsLessFunc(data, a, b, less)
|
||||
limit--
|
||||
}
|
||||
|
||||
pivot, hint := choosePivotLessFunc(data, a, b, less)
|
||||
if hint == decreasingHint {
|
||||
reverseRangeLessFunc(data, a, b, less)
|
||||
// The chosen pivot was pivot-a elements after the start of the array.
|
||||
// After reversing it is pivot-a elements before the end of the array.
|
||||
// The idea came from Rust's implementation.
|
||||
pivot = (b - 1) - (pivot - a)
|
||||
hint = increasingHint
|
||||
}
|
||||
|
||||
// The slice is likely already sorted.
|
||||
if wasBalanced && wasPartitioned && hint == increasingHint {
|
||||
if partialInsertionSortLessFunc(data, a, b, less) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Probably the slice contains many duplicate elements, partition the slice into
|
||||
// elements equal to and elements greater than the pivot.
|
||||
if a > 0 && !less(data[a-1], data[pivot]) {
|
||||
mid := partitionEqualLessFunc(data, a, b, pivot, less)
|
||||
a = mid
|
||||
continue
|
||||
}
|
||||
|
||||
mid, alreadyPartitioned := partitionLessFunc(data, a, b, pivot, less)
|
||||
wasPartitioned = alreadyPartitioned
|
||||
|
||||
leftLen, rightLen := mid-a, b-mid
|
||||
balanceThreshold := length / 8
|
||||
if leftLen < rightLen {
|
||||
wasBalanced = leftLen >= balanceThreshold
|
||||
pdqsortLessFunc(data, a, mid, limit, less)
|
||||
a = mid + 1
|
||||
} else {
|
||||
wasBalanced = rightLen >= balanceThreshold
|
||||
pdqsortLessFunc(data, mid+1, b, limit, less)
|
||||
b = mid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partitionLessFunc does one quicksort partition.
|
||||
// Let p = data[pivot]
|
||||
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
||||
// On return, data[newpivot] = p
|
||||
func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int, alreadyPartitioned bool) {
|
||||
data[a], data[pivot] = data[pivot], data[a]
|
||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||
|
||||
for i <= j && less(data[i], data[a]) {
|
||||
i++
|
||||
}
|
||||
for i <= j && !less(data[j], data[a]) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
data[j], data[a] = data[a], data[j]
|
||||
return j, true
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
|
||||
for {
|
||||
for i <= j && less(data[i], data[a]) {
|
||||
i++
|
||||
}
|
||||
for i <= j && !less(data[j], data[a]) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
break
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
data[j], data[a] = data[a], data[j]
|
||||
return j, false
|
||||
}
|
||||
|
||||
// partitionEqualLessFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
||||
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
||||
func partitionEqualLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int) {
|
||||
data[a], data[pivot] = data[pivot], data[a]
|
||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||
|
||||
for {
|
||||
for i <= j && !less(data[a], data[i]) {
|
||||
i++
|
||||
}
|
||||
for i <= j && less(data[a], data[j]) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
break
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// partialInsertionSortLessFunc partially sorts a slice, returns true if the slice is sorted at the end.
|
||||
func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) bool {
|
||||
const (
|
||||
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
||||
shortestShifting = 50 // don't shift any elements on short arrays
|
||||
)
|
||||
i := a + 1
|
||||
for j := 0; j < maxSteps; j++ {
|
||||
for i < b && !less(data[i], data[i-1]) {
|
||||
i++
|
||||
}
|
||||
|
||||
if i == b {
|
||||
return true
|
||||
}
|
||||
|
||||
if b-a < shortestShifting {
|
||||
return false
|
||||
}
|
||||
|
||||
data[i], data[i-1] = data[i-1], data[i]
|
||||
|
||||
// Shift the smaller one to the left.
|
||||
if i-a >= 2 {
|
||||
for j := i - 1; j >= 1; j-- {
|
||||
if !less(data[j], data[j-1]) {
|
||||
break
|
||||
}
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
// Shift the greater one to the right.
|
||||
if b-i >= 2 {
|
||||
for j := i + 1; j < b; j++ {
|
||||
if !less(data[j], data[j-1]) {
|
||||
break
|
||||
}
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// breakPatternsLessFunc scatters some elements around in an attempt to break some patterns
|
||||
// that might cause imbalanced partitions in quicksort.
|
||||
func breakPatternsLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
|
||||
length := b - a
|
||||
if length >= 8 {
|
||||
random := xorshift(length)
|
||||
modulus := nextPowerOfTwo(length)
|
||||
|
||||
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
||||
other := int(uint(random.Next()) & (modulus - 1))
|
||||
if other >= length {
|
||||
other -= length
|
||||
}
|
||||
data[idx], data[a+other] = data[a+other], data[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// choosePivotLessFunc chooses a pivot in data[a:b].
|
||||
//
|
||||
// [0,8): chooses a static pivot.
|
||||
// [8,shortestNinther): uses the simple median-of-three method.
|
||||
// [shortestNinther,∞): uses the Tukey ninther method.
|
||||
func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (pivot int, hint sortedHint) {
|
||||
const (
|
||||
shortestNinther = 50
|
||||
maxSwaps = 4 * 3
|
||||
)
|
||||
|
||||
l := b - a
|
||||
|
||||
var (
|
||||
swaps int
|
||||
i = a + l/4*1
|
||||
j = a + l/4*2
|
||||
k = a + l/4*3
|
||||
)
|
||||
|
||||
if l >= 8 {
|
||||
if l >= shortestNinther {
|
||||
// Tukey ninther method, the idea came from Rust's implementation.
|
||||
i = medianAdjacentLessFunc(data, i, &swaps, less)
|
||||
j = medianAdjacentLessFunc(data, j, &swaps, less)
|
||||
k = medianAdjacentLessFunc(data, k, &swaps, less)
|
||||
}
|
||||
// Find the median among i, j, k and stores it into j.
|
||||
j = medianLessFunc(data, i, j, k, &swaps, less)
|
||||
}
|
||||
|
||||
switch swaps {
|
||||
case 0:
|
||||
return j, increasingHint
|
||||
case maxSwaps:
|
||||
return j, decreasingHint
|
||||
default:
|
||||
return j, unknownHint
|
||||
}
|
||||
}
|
||||
|
||||
// order2LessFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
||||
func order2LessFunc[E any](data []E, a, b int, swaps *int, less func(a, b E) bool) (int, int) {
|
||||
if less(data[b], data[a]) {
|
||||
*swaps++
|
||||
return b, a
|
||||
}
|
||||
return a, b
|
||||
}
|
||||
|
||||
// medianLessFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
||||
func medianLessFunc[E any](data []E, a, b, c int, swaps *int, less func(a, b E) bool) int {
|
||||
a, b = order2LessFunc(data, a, b, swaps, less)
|
||||
b, c = order2LessFunc(data, b, c, swaps, less)
|
||||
a, b = order2LessFunc(data, a, b, swaps, less)
|
||||
return b
|
||||
}
|
||||
|
||||
// medianAdjacentLessFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
||||
func medianAdjacentLessFunc[E any](data []E, a int, swaps *int, less func(a, b E) bool) int {
|
||||
return medianLessFunc(data, a-1, a, a+1, swaps, less)
|
||||
}
|
||||
|
||||
func reverseRangeLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
|
||||
i := a
|
||||
j := b - 1
|
||||
for i < j {
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
}
|
||||
|
||||
func swapRangeLessFunc[E any](data []E, a, b, n int, less func(a, b E) bool) {
|
||||
for i := 0; i < n; i++ {
|
||||
data[a+i], data[b+i] = data[b+i], data[a+i]
|
||||
}
|
||||
}
|
||||
|
||||
func stableLessFunc[E any](data []E, n int, less func(a, b E) bool) {
|
||||
blockSize := 20 // must be > 0
|
||||
a, b := 0, blockSize
|
||||
for b <= n {
|
||||
insertionSortLessFunc(data, a, b, less)
|
||||
a = b
|
||||
b += blockSize
|
||||
}
|
||||
insertionSortLessFunc(data, a, n, less)
|
||||
|
||||
for blockSize < n {
|
||||
a, b = 0, 2*blockSize
|
||||
for b <= n {
|
||||
symMergeLessFunc(data, a, a+blockSize, b, less)
|
||||
a = b
|
||||
b += 2 * blockSize
|
||||
}
|
||||
if m := a + blockSize; m < n {
|
||||
symMergeLessFunc(data, a, m, n, less)
|
||||
}
|
||||
blockSize *= 2
|
||||
}
|
||||
}
|
||||
|
||||
// symMergeLessFunc merges the two sorted subsequences data[a:m] and data[m:b] using
|
||||
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
|
||||
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
|
||||
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
|
||||
// Computer Science, pages 714-723. Springer, 2004.
|
||||
//
|
||||
// Let M = m-a and N = b-n. Wolog M < N.
|
||||
// The recursion depth is bound by ceil(log(N+M)).
|
||||
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
|
||||
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
|
||||
//
|
||||
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
|
||||
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
|
||||
// in the paper carries through for Swap operations, especially as the block
|
||||
// swapping rotate uses only O(M+N) Swaps.
|
||||
//
|
||||
// symMerge assumes non-degenerate arguments: a < m && m < b.
|
||||
// Having the caller check this condition eliminates many leaf recursion calls,
|
||||
// which improves performance.
|
||||
func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[a] into data[m:b]
|
||||
// if data[a:m] only contains one element.
|
||||
if m-a == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] >= data[a] for m <= i < b.
|
||||
// Exit the search loop with i == b in case no such index exists.
|
||||
i := m
|
||||
j := b
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1)
|
||||
if less(data[h], data[a]) {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[a] reaches the position before i.
|
||||
for k := a; k < i-1; k++ {
|
||||
data[k], data[k+1] = data[k+1], data[k]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[m] into data[a:m]
|
||||
// if data[m:b] only contains one element.
|
||||
if b-m == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] > data[m] for a <= i < m.
|
||||
// Exit the search loop with i == m in case no such index exists.
|
||||
i := a
|
||||
j := m
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1)
|
||||
if !less(data[m], data[h]) {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[m] reaches the position i.
|
||||
for k := m; k > i; k-- {
|
||||
data[k], data[k-1] = data[k-1], data[k]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mid := int(uint(a+b) >> 1)
|
||||
n := mid + m
|
||||
var start, r int
|
||||
if m > mid {
|
||||
start = n - b
|
||||
r = mid
|
||||
} else {
|
||||
start = a
|
||||
r = m
|
||||
}
|
||||
p := n - 1
|
||||
|
||||
for start < r {
|
||||
c := int(uint(start+r) >> 1)
|
||||
if !less(data[p-c], data[c]) {
|
||||
start = c + 1
|
||||
} else {
|
||||
r = c
|
||||
}
|
||||
}
|
||||
|
||||
end := n - start
|
||||
if start < m && m < end {
|
||||
rotateLessFunc(data, start, m, end, less)
|
||||
}
|
||||
if a < start && start < mid {
|
||||
symMergeLessFunc(data, a, start, mid, less)
|
||||
}
|
||||
if mid < end && end < b {
|
||||
symMergeLessFunc(data, mid, end, b, less)
|
||||
}
|
||||
}
|
||||
|
||||
// rotateLessFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
|
||||
// Data of the form 'x u v y' is changed to 'x v u y'.
|
||||
// rotate performs at most b-a many calls to data.Swap,
|
||||
// and it assumes non-degenerate arguments: a < m && m < b.
|
||||
func rotateLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
|
||||
i := m - a
|
||||
j := b - m
|
||||
|
||||
for i != j {
|
||||
if i > j {
|
||||
swapRangeLessFunc(data, m-i, m, j, less)
|
||||
i -= j
|
||||
} else {
|
||||
swapRangeLessFunc(data, m-i, m+j-i, i, less)
|
||||
j -= i
|
||||
}
|
||||
}
|
||||
// i == j
|
||||
swapRangeLessFunc(data, m-i, m, i, less)
|
||||
}
|
||||
+481
@@ -0,0 +1,481 @@
|
||||
// Code generated by gen_sort_variants.go; DO NOT EDIT.
|
||||
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package slices
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
|
||||
// insertionSortOrdered sorts data[a:b] using insertion sort.
|
||||
func insertionSortOrdered[E constraints.Ordered](data []E, a, b int) {
|
||||
for i := a + 1; i < b; i++ {
|
||||
for j := i; j > a && (data[j] < data[j-1]); j-- {
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// siftDownOrdered implements the heap property on data[lo:hi].
|
||||
// first is an offset into the array where the root of the heap lies.
|
||||
func siftDownOrdered[E constraints.Ordered](data []E, lo, hi, first int) {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && (data[first+child] < data[first+child+1]) {
|
||||
child++
|
||||
}
|
||||
if !(data[first+root] < data[first+child]) {
|
||||
return
|
||||
}
|
||||
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func heapSortOrdered[E constraints.Ordered](data []E, a, b int) {
|
||||
first := a
|
||||
lo := 0
|
||||
hi := b - a
|
||||
|
||||
// Build heap with greatest element at top.
|
||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||
siftDownOrdered(data, i, hi, first)
|
||||
}
|
||||
|
||||
// Pop elements, largest first, into end of data.
|
||||
for i := hi - 1; i >= 0; i-- {
|
||||
data[first], data[first+i] = data[first+i], data[first]
|
||||
siftDownOrdered(data, lo, i, first)
|
||||
}
|
||||
}
|
||||
|
||||
// pdqsortOrdered sorts data[a:b].
|
||||
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
||||
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
||||
// C++ implementation: https://github.com/orlp/pdqsort
|
||||
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
||||
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
||||
func pdqsortOrdered[E constraints.Ordered](data []E, a, b, limit int) {
|
||||
const maxInsertion = 12
|
||||
|
||||
var (
|
||||
wasBalanced = true // whether the last partitioning was reasonably balanced
|
||||
wasPartitioned = true // whether the slice was already partitioned
|
||||
)
|
||||
|
||||
for {
|
||||
length := b - a
|
||||
|
||||
if length <= maxInsertion {
|
||||
insertionSortOrdered(data, a, b)
|
||||
return
|
||||
}
|
||||
|
||||
// Fall back to heapsort if too many bad choices were made.
|
||||
if limit == 0 {
|
||||
heapSortOrdered(data, a, b)
|
||||
return
|
||||
}
|
||||
|
||||
// If the last partitioning was imbalanced, we need to breaking patterns.
|
||||
if !wasBalanced {
|
||||
breakPatternsOrdered(data, a, b)
|
||||
limit--
|
||||
}
|
||||
|
||||
pivot, hint := choosePivotOrdered(data, a, b)
|
||||
if hint == decreasingHint {
|
||||
reverseRangeOrdered(data, a, b)
|
||||
// The chosen pivot was pivot-a elements after the start of the array.
|
||||
// After reversing it is pivot-a elements before the end of the array.
|
||||
// The idea came from Rust's implementation.
|
||||
pivot = (b - 1) - (pivot - a)
|
||||
hint = increasingHint
|
||||
}
|
||||
|
||||
// The slice is likely already sorted.
|
||||
if wasBalanced && wasPartitioned && hint == increasingHint {
|
||||
if partialInsertionSortOrdered(data, a, b) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Probably the slice contains many duplicate elements, partition the slice into
|
||||
// elements equal to and elements greater than the pivot.
|
||||
if a > 0 && !(data[a-1] < data[pivot]) {
|
||||
mid := partitionEqualOrdered(data, a, b, pivot)
|
||||
a = mid
|
||||
continue
|
||||
}
|
||||
|
||||
mid, alreadyPartitioned := partitionOrdered(data, a, b, pivot)
|
||||
wasPartitioned = alreadyPartitioned
|
||||
|
||||
leftLen, rightLen := mid-a, b-mid
|
||||
balanceThreshold := length / 8
|
||||
if leftLen < rightLen {
|
||||
wasBalanced = leftLen >= balanceThreshold
|
||||
pdqsortOrdered(data, a, mid, limit)
|
||||
a = mid + 1
|
||||
} else {
|
||||
wasBalanced = rightLen >= balanceThreshold
|
||||
pdqsortOrdered(data, mid+1, b, limit)
|
||||
b = mid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partitionOrdered does one quicksort partition.
|
||||
// Let p = data[pivot]
|
||||
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
||||
// On return, data[newpivot] = p
|
||||
func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) {
|
||||
data[a], data[pivot] = data[pivot], data[a]
|
||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||
|
||||
for i <= j && (data[i] < data[a]) {
|
||||
i++
|
||||
}
|
||||
for i <= j && !(data[j] < data[a]) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
data[j], data[a] = data[a], data[j]
|
||||
return j, true
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
|
||||
for {
|
||||
for i <= j && (data[i] < data[a]) {
|
||||
i++
|
||||
}
|
||||
for i <= j && !(data[j] < data[a]) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
break
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
data[j], data[a] = data[a], data[j]
|
||||
return j, false
|
||||
}
|
||||
|
||||
// partitionEqualOrdered partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
||||
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
||||
func partitionEqualOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int) {
|
||||
data[a], data[pivot] = data[pivot], data[a]
|
||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||
|
||||
for {
|
||||
for i <= j && !(data[a] < data[i]) {
|
||||
i++
|
||||
}
|
||||
for i <= j && (data[a] < data[j]) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
break
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// partialInsertionSortOrdered partially sorts a slice, returns true if the slice is sorted at the end.
|
||||
func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool {
|
||||
const (
|
||||
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
||||
shortestShifting = 50 // don't shift any elements on short arrays
|
||||
)
|
||||
i := a + 1
|
||||
for j := 0; j < maxSteps; j++ {
|
||||
for i < b && !(data[i] < data[i-1]) {
|
||||
i++
|
||||
}
|
||||
|
||||
if i == b {
|
||||
return true
|
||||
}
|
||||
|
||||
if b-a < shortestShifting {
|
||||
return false
|
||||
}
|
||||
|
||||
data[i], data[i-1] = data[i-1], data[i]
|
||||
|
||||
// Shift the smaller one to the left.
|
||||
if i-a >= 2 {
|
||||
for j := i - 1; j >= 1; j-- {
|
||||
if !(data[j] < data[j-1]) {
|
||||
break
|
||||
}
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
// Shift the greater one to the right.
|
||||
if b-i >= 2 {
|
||||
for j := i + 1; j < b; j++ {
|
||||
if !(data[j] < data[j-1]) {
|
||||
break
|
||||
}
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// breakPatternsOrdered scatters some elements around in an attempt to break some patterns
|
||||
// that might cause imbalanced partitions in quicksort.
|
||||
func breakPatternsOrdered[E constraints.Ordered](data []E, a, b int) {
|
||||
length := b - a
|
||||
if length >= 8 {
|
||||
random := xorshift(length)
|
||||
modulus := nextPowerOfTwo(length)
|
||||
|
||||
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
||||
other := int(uint(random.Next()) & (modulus - 1))
|
||||
if other >= length {
|
||||
other -= length
|
||||
}
|
||||
data[idx], data[a+other] = data[a+other], data[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// choosePivotOrdered chooses a pivot in data[a:b].
|
||||
//
|
||||
// [0,8): chooses a static pivot.
|
||||
// [8,shortestNinther): uses the simple median-of-three method.
|
||||
// [shortestNinther,∞): uses the Tukey ninther method.
|
||||
func choosePivotOrdered[E constraints.Ordered](data []E, a, b int) (pivot int, hint sortedHint) {
|
||||
const (
|
||||
shortestNinther = 50
|
||||
maxSwaps = 4 * 3
|
||||
)
|
||||
|
||||
l := b - a
|
||||
|
||||
var (
|
||||
swaps int
|
||||
i = a + l/4*1
|
||||
j = a + l/4*2
|
||||
k = a + l/4*3
|
||||
)
|
||||
|
||||
if l >= 8 {
|
||||
if l >= shortestNinther {
|
||||
// Tukey ninther method, the idea came from Rust's implementation.
|
||||
i = medianAdjacentOrdered(data, i, &swaps)
|
||||
j = medianAdjacentOrdered(data, j, &swaps)
|
||||
k = medianAdjacentOrdered(data, k, &swaps)
|
||||
}
|
||||
// Find the median among i, j, k and stores it into j.
|
||||
j = medianOrdered(data, i, j, k, &swaps)
|
||||
}
|
||||
|
||||
switch swaps {
|
||||
case 0:
|
||||
return j, increasingHint
|
||||
case maxSwaps:
|
||||
return j, decreasingHint
|
||||
default:
|
||||
return j, unknownHint
|
||||
}
|
||||
}
|
||||
|
||||
// order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
||||
func order2Ordered[E constraints.Ordered](data []E, a, b int, swaps *int) (int, int) {
|
||||
if data[b] < data[a] {
|
||||
*swaps++
|
||||
return b, a
|
||||
}
|
||||
return a, b
|
||||
}
|
||||
|
||||
// medianOrdered returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
||||
func medianOrdered[E constraints.Ordered](data []E, a, b, c int, swaps *int) int {
|
||||
a, b = order2Ordered(data, a, b, swaps)
|
||||
b, c = order2Ordered(data, b, c, swaps)
|
||||
a, b = order2Ordered(data, a, b, swaps)
|
||||
return b
|
||||
}
|
||||
|
||||
// medianAdjacentOrdered finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
||||
func medianAdjacentOrdered[E constraints.Ordered](data []E, a int, swaps *int) int {
|
||||
return medianOrdered(data, a-1, a, a+1, swaps)
|
||||
}
|
||||
|
||||
func reverseRangeOrdered[E constraints.Ordered](data []E, a, b int) {
|
||||
i := a
|
||||
j := b - 1
|
||||
for i < j {
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
}
|
||||
|
||||
func swapRangeOrdered[E constraints.Ordered](data []E, a, b, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
data[a+i], data[b+i] = data[b+i], data[a+i]
|
||||
}
|
||||
}
|
||||
|
||||
func stableOrdered[E constraints.Ordered](data []E, n int) {
|
||||
blockSize := 20 // must be > 0
|
||||
a, b := 0, blockSize
|
||||
for b <= n {
|
||||
insertionSortOrdered(data, a, b)
|
||||
a = b
|
||||
b += blockSize
|
||||
}
|
||||
insertionSortOrdered(data, a, n)
|
||||
|
||||
for blockSize < n {
|
||||
a, b = 0, 2*blockSize
|
||||
for b <= n {
|
||||
symMergeOrdered(data, a, a+blockSize, b)
|
||||
a = b
|
||||
b += 2 * blockSize
|
||||
}
|
||||
if m := a + blockSize; m < n {
|
||||
symMergeOrdered(data, a, m, n)
|
||||
}
|
||||
blockSize *= 2
|
||||
}
|
||||
}
|
||||
|
||||
// symMergeOrdered merges the two sorted subsequences data[a:m] and data[m:b] using
|
||||
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
|
||||
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
|
||||
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
|
||||
// Computer Science, pages 714-723. Springer, 2004.
|
||||
//
|
||||
// Let M = m-a and N = b-n. Wolog M < N.
|
||||
// The recursion depth is bound by ceil(log(N+M)).
|
||||
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
|
||||
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
|
||||
//
|
||||
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
|
||||
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
|
||||
// in the paper carries through for Swap operations, especially as the block
|
||||
// swapping rotate uses only O(M+N) Swaps.
|
||||
//
|
||||
// symMerge assumes non-degenerate arguments: a < m && m < b.
|
||||
// Having the caller check this condition eliminates many leaf recursion calls,
|
||||
// which improves performance.
|
||||
func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) {
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[a] into data[m:b]
|
||||
// if data[a:m] only contains one element.
|
||||
if m-a == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] >= data[a] for m <= i < b.
|
||||
// Exit the search loop with i == b in case no such index exists.
|
||||
i := m
|
||||
j := b
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1)
|
||||
if data[h] < data[a] {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[a] reaches the position before i.
|
||||
for k := a; k < i-1; k++ {
|
||||
data[k], data[k+1] = data[k+1], data[k]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[m] into data[a:m]
|
||||
// if data[m:b] only contains one element.
|
||||
if b-m == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] > data[m] for a <= i < m.
|
||||
// Exit the search loop with i == m in case no such index exists.
|
||||
i := a
|
||||
j := m
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1)
|
||||
if !(data[m] < data[h]) {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[m] reaches the position i.
|
||||
for k := m; k > i; k-- {
|
||||
data[k], data[k-1] = data[k-1], data[k]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mid := int(uint(a+b) >> 1)
|
||||
n := mid + m
|
||||
var start, r int
|
||||
if m > mid {
|
||||
start = n - b
|
||||
r = mid
|
||||
} else {
|
||||
start = a
|
||||
r = m
|
||||
}
|
||||
p := n - 1
|
||||
|
||||
for start < r {
|
||||
c := int(uint(start+r) >> 1)
|
||||
if !(data[p-c] < data[c]) {
|
||||
start = c + 1
|
||||
} else {
|
||||
r = c
|
||||
}
|
||||
}
|
||||
|
||||
end := n - start
|
||||
if start < m && m < end {
|
||||
rotateOrdered(data, start, m, end)
|
||||
}
|
||||
if a < start && start < mid {
|
||||
symMergeOrdered(data, a, start, mid)
|
||||
}
|
||||
if mid < end && end < b {
|
||||
symMergeOrdered(data, mid, end, b)
|
||||
}
|
||||
}
|
||||
|
||||
// rotateOrdered rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
|
||||
// Data of the form 'x u v y' is changed to 'x v u y'.
|
||||
// rotate performs at most b-a many calls to data.Swap,
|
||||
// and it assumes non-degenerate arguments: a < m && m < b.
|
||||
func rotateOrdered[E constraints.Ordered](data []E, a, m, b int) {
|
||||
i := m - a
|
||||
j := b - m
|
||||
|
||||
for i != j {
|
||||
if i > j {
|
||||
swapRangeOrdered(data, m-i, m, j)
|
||||
i -= j
|
||||
} else {
|
||||
swapRangeOrdered(data, m-i, m+j-i, i)
|
||||
j -= i
|
||||
}
|
||||
}
|
||||
// i == j
|
||||
swapRangeOrdered(data, m-i, m, i)
|
||||
}
|
||||
Vendored
+11
-1
@@ -443,6 +443,10 @@ golang.org/x/crypto/nacl/secretbox
|
||||
golang.org/x/crypto/pkcs12
|
||||
golang.org/x/crypto/pkcs12/internal/rc2
|
||||
golang.org/x/crypto/salsa20/salsa
|
||||
# golang.org/x/exp v0.0.0-20230131160201-f062dba9d201
|
||||
## explicit; go 1.18
|
||||
golang.org/x/exp/constraints
|
||||
golang.org/x/exp/slices
|
||||
# golang.org/x/net v0.1.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/net/context
|
||||
@@ -1218,14 +1222,19 @@ k8s.io/utils/path
|
||||
k8s.io/utils/pointer
|
||||
k8s.io/utils/strings/slices
|
||||
k8s.io/utils/trace
|
||||
# open-cluster-management.io/api v0.9.1-0.20230113024003-6529d52caeeb
|
||||
# open-cluster-management.io/api v0.9.1-0.20230119012002-efddffc71682
|
||||
## explicit; go 1.19
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/fake
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/scheme
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1/fake
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/fake
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta1
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta1/fake
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta2
|
||||
open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta2/fake
|
||||
open-cluster-management.io/api/client/cluster/informers/externalversions
|
||||
open-cluster-management.io/api/client/cluster/informers/externalversions/cluster
|
||||
open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1
|
||||
@@ -1256,6 +1265,7 @@ open-cluster-management.io/api/cluster/v1alpha1
|
||||
open-cluster-management.io/api/cluster/v1beta1
|
||||
open-cluster-management.io/api/cluster/v1beta2
|
||||
open-cluster-management.io/api/feature
|
||||
open-cluster-management.io/api/utils/work/v1/workapplier
|
||||
open-cluster-management.io/api/work/v1
|
||||
open-cluster-management.io/api/work/v1alpha1
|
||||
# sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30
|
||||
|
||||
vendor/open-cluster-management.io/api/client/cluster/clientset/versioned/fake/clientset_generated.go
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/discovery"
|
||||
fakediscovery "k8s.io/client-go/discovery/fake"
|
||||
"k8s.io/client-go/testing"
|
||||
clientset "open-cluster-management.io/api/client/cluster/clientset/versioned"
|
||||
clusterv1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1"
|
||||
fakeclusterv1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1/fake"
|
||||
clusterv1alpha1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1"
|
||||
fakeclusterv1alpha1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1/fake"
|
||||
clusterv1beta1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta1"
|
||||
fakeclusterv1beta1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta1/fake"
|
||||
clusterv1beta2 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta2"
|
||||
fakeclusterv1beta2 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta2/fake"
|
||||
)
|
||||
|
||||
// NewSimpleClientset returns a clientset that will respond with the provided objects.
|
||||
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
|
||||
// without applying any validations and/or defaults. It shouldn't be considered a replacement
|
||||
// for a real clientset and is mostly useful in simple unit tests.
|
||||
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
|
||||
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||
for _, obj := range objects {
|
||||
if err := o.Add(obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
cs := &Clientset{tracker: o}
|
||||
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
|
||||
cs.AddReactor("*", "*", testing.ObjectReaction(o))
|
||||
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
|
||||
gvr := action.GetResource()
|
||||
ns := action.GetNamespace()
|
||||
watch, err := o.Watch(gvr, ns)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
return true, watch, nil
|
||||
})
|
||||
|
||||
return cs
|
||||
}
|
||||
|
||||
// Clientset implements clientset.Interface. Meant to be embedded into a
|
||||
// struct to get a default implementation. This makes faking out just the method
|
||||
// you want to test easier.
|
||||
type Clientset struct {
|
||||
testing.Fake
|
||||
discovery *fakediscovery.FakeDiscovery
|
||||
tracker testing.ObjectTracker
|
||||
}
|
||||
|
||||
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
||||
return c.discovery
|
||||
}
|
||||
|
||||
func (c *Clientset) Tracker() testing.ObjectTracker {
|
||||
return c.tracker
|
||||
}
|
||||
|
||||
var (
|
||||
_ clientset.Interface = &Clientset{}
|
||||
_ testing.FakeClient = &Clientset{}
|
||||
)
|
||||
|
||||
// ClusterV1 retrieves the ClusterV1Client
|
||||
func (c *Clientset) ClusterV1() clusterv1.ClusterV1Interface {
|
||||
return &fakeclusterv1.FakeClusterV1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// ClusterV1alpha1 retrieves the ClusterV1alpha1Client
|
||||
func (c *Clientset) ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface {
|
||||
return &fakeclusterv1alpha1.FakeClusterV1alpha1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// ClusterV1beta1 retrieves the ClusterV1beta1Client
|
||||
func (c *Clientset) ClusterV1beta1() clusterv1beta1.ClusterV1beta1Interface {
|
||||
return &fakeclusterv1beta1.FakeClusterV1beta1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// ClusterV1beta2 retrieves the ClusterV1beta2Client
|
||||
func (c *Clientset) ClusterV1beta2() clusterv1beta2.ClusterV1beta2Interface {
|
||||
return &fakeclusterv1beta2.FakeClusterV1beta2{Fake: &c.Fake}
|
||||
}
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// This package has the automatically generated fake clientset.
|
||||
package fake
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
clusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
clusterv1beta2 "open-cluster-management.io/api/cluster/v1beta2"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
var codecs = serializer.NewCodecFactory(scheme)
|
||||
|
||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
clusterv1.AddToScheme,
|
||||
clusterv1alpha1.AddToScheme,
|
||||
clusterv1beta1.AddToScheme,
|
||||
clusterv1beta2.AddToScheme,
|
||||
}
|
||||
|
||||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||
// of clientsets, like in:
|
||||
//
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
//
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
//
|
||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||
// correctly.
|
||||
var AddToScheme = localSchemeBuilder.AddToScheme
|
||||
|
||||
func init() {
|
||||
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
|
||||
utilruntime.Must(AddToScheme(scheme))
|
||||
}
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
rest "k8s.io/client-go/rest"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1"
|
||||
)
|
||||
|
||||
type FakeClusterV1 struct {
|
||||
*testing.Fake
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1) ManagedClusters() v1.ManagedClusterInterface {
|
||||
return &FakeManagedClusters{c}
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeClusterV1) RESTClient() rest.Interface {
|
||||
var ret *rest.RESTClient
|
||||
return ret
|
||||
}
|
||||
Generated
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
clusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
)
|
||||
|
||||
// FakeManagedClusters implements ManagedClusterInterface
|
||||
type FakeManagedClusters struct {
|
||||
Fake *FakeClusterV1
|
||||
}
|
||||
|
||||
var managedclustersResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1", Resource: "managedclusters"}
|
||||
|
||||
var managedclustersKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1", Kind: "ManagedCluster"}
|
||||
|
||||
// Get takes name of the managedCluster, and returns the corresponding managedCluster object, and an error if there is any.
|
||||
func (c *FakeManagedClusters) Get(ctx context.Context, name string, options v1.GetOptions) (result *clusterv1.ManagedCluster, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootGetAction(managedclustersResource, name), &clusterv1.ManagedCluster{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*clusterv1.ManagedCluster), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ManagedClusters that match those selectors.
|
||||
func (c *FakeManagedClusters) List(ctx context.Context, opts v1.ListOptions) (result *clusterv1.ManagedClusterList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootListAction(managedclustersResource, managedclustersKind, opts), &clusterv1.ManagedClusterList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &clusterv1.ManagedClusterList{ListMeta: obj.(*clusterv1.ManagedClusterList).ListMeta}
|
||||
for _, item := range obj.(*clusterv1.ManagedClusterList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested managedClusters.
|
||||
func (c *FakeManagedClusters) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewRootWatchAction(managedclustersResource, opts))
|
||||
}
|
||||
|
||||
// Create takes the representation of a managedCluster and creates it. Returns the server's representation of the managedCluster, and an error, if there is any.
|
||||
func (c *FakeManagedClusters) Create(ctx context.Context, managedCluster *clusterv1.ManagedCluster, opts v1.CreateOptions) (result *clusterv1.ManagedCluster, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootCreateAction(managedclustersResource, managedCluster), &clusterv1.ManagedCluster{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*clusterv1.ManagedCluster), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a managedCluster and updates it. Returns the server's representation of the managedCluster, and an error, if there is any.
|
||||
func (c *FakeManagedClusters) Update(ctx context.Context, managedCluster *clusterv1.ManagedCluster, opts v1.UpdateOptions) (result *clusterv1.ManagedCluster, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateAction(managedclustersResource, managedCluster), &clusterv1.ManagedCluster{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*clusterv1.ManagedCluster), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeManagedClusters) UpdateStatus(ctx context.Context, managedCluster *clusterv1.ManagedCluster, opts v1.UpdateOptions) (*clusterv1.ManagedCluster, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateSubresourceAction(managedclustersResource, "status", managedCluster), &clusterv1.ManagedCluster{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*clusterv1.ManagedCluster), err
|
||||
}
|
||||
|
||||
// Delete takes name of the managedCluster and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeManagedClusters) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewRootDeleteActionWithOptions(managedclustersResource, name, opts), &clusterv1.ManagedCluster{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeManagedClusters) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewRootDeleteCollectionAction(managedclustersResource, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &clusterv1.ManagedClusterList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched managedCluster.
|
||||
func (c *FakeManagedClusters) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *clusterv1.ManagedCluster, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootPatchSubresourceAction(managedclustersResource, name, pt, data, subresources...), &clusterv1.ManagedCluster{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*clusterv1.ManagedCluster), err
|
||||
}
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake
|
||||
Generated
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
)
|
||||
|
||||
// FakeAddOnPlacementScores implements AddOnPlacementScoreInterface
|
||||
type FakeAddOnPlacementScores struct {
|
||||
Fake *FakeClusterV1alpha1
|
||||
ns string
|
||||
}
|
||||
|
||||
var addonplacementscoresResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1alpha1", Resource: "addonplacementscores"}
|
||||
|
||||
var addonplacementscoresKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1alpha1", Kind: "AddOnPlacementScore"}
|
||||
|
||||
// Get takes name of the addOnPlacementScore, and returns the corresponding addOnPlacementScore object, and an error if there is any.
|
||||
func (c *FakeAddOnPlacementScores) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.AddOnPlacementScore, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(addonplacementscoresResource, c.ns, name), &v1alpha1.AddOnPlacementScore{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.AddOnPlacementScore), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of AddOnPlacementScores that match those selectors.
|
||||
func (c *FakeAddOnPlacementScores) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.AddOnPlacementScoreList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(addonplacementscoresResource, addonplacementscoresKind, c.ns, opts), &v1alpha1.AddOnPlacementScoreList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1alpha1.AddOnPlacementScoreList{ListMeta: obj.(*v1alpha1.AddOnPlacementScoreList).ListMeta}
|
||||
for _, item := range obj.(*v1alpha1.AddOnPlacementScoreList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested addOnPlacementScores.
|
||||
func (c *FakeAddOnPlacementScores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(addonplacementscoresResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a addOnPlacementScore and creates it. Returns the server's representation of the addOnPlacementScore, and an error, if there is any.
|
||||
func (c *FakeAddOnPlacementScores) Create(ctx context.Context, addOnPlacementScore *v1alpha1.AddOnPlacementScore, opts v1.CreateOptions) (result *v1alpha1.AddOnPlacementScore, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(addonplacementscoresResource, c.ns, addOnPlacementScore), &v1alpha1.AddOnPlacementScore{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.AddOnPlacementScore), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a addOnPlacementScore and updates it. Returns the server's representation of the addOnPlacementScore, and an error, if there is any.
|
||||
func (c *FakeAddOnPlacementScores) Update(ctx context.Context, addOnPlacementScore *v1alpha1.AddOnPlacementScore, opts v1.UpdateOptions) (result *v1alpha1.AddOnPlacementScore, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(addonplacementscoresResource, c.ns, addOnPlacementScore), &v1alpha1.AddOnPlacementScore{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.AddOnPlacementScore), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeAddOnPlacementScores) UpdateStatus(ctx context.Context, addOnPlacementScore *v1alpha1.AddOnPlacementScore, opts v1.UpdateOptions) (*v1alpha1.AddOnPlacementScore, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(addonplacementscoresResource, "status", c.ns, addOnPlacementScore), &v1alpha1.AddOnPlacementScore{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.AddOnPlacementScore), err
|
||||
}
|
||||
|
||||
// Delete takes name of the addOnPlacementScore and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeAddOnPlacementScores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteActionWithOptions(addonplacementscoresResource, c.ns, name, opts), &v1alpha1.AddOnPlacementScore{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeAddOnPlacementScores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(addonplacementscoresResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1alpha1.AddOnPlacementScoreList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched addOnPlacementScore.
|
||||
func (c *FakeAddOnPlacementScores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.AddOnPlacementScore, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(addonplacementscoresResource, c.ns, name, pt, data, subresources...), &v1alpha1.AddOnPlacementScore{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.AddOnPlacementScore), err
|
||||
}
|
||||
Generated
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
rest "k8s.io/client-go/rest"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1alpha1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1alpha1"
|
||||
)
|
||||
|
||||
type FakeClusterV1alpha1 struct {
|
||||
*testing.Fake
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1alpha1) AddOnPlacementScores(namespace string) v1alpha1.AddOnPlacementScoreInterface {
|
||||
return &FakeAddOnPlacementScores{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1alpha1) ClusterClaims() v1alpha1.ClusterClaimInterface {
|
||||
return &FakeClusterClaims{c}
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeClusterV1alpha1) RESTClient() rest.Interface {
|
||||
var ret *rest.RESTClient
|
||||
return ret
|
||||
}
|
||||
Generated
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
)
|
||||
|
||||
// FakeClusterClaims implements ClusterClaimInterface
|
||||
type FakeClusterClaims struct {
|
||||
Fake *FakeClusterV1alpha1
|
||||
}
|
||||
|
||||
var clusterclaimsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1alpha1", Resource: "clusterclaims"}
|
||||
|
||||
var clusterclaimsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1alpha1", Kind: "ClusterClaim"}
|
||||
|
||||
// Get takes name of the clusterClaim, and returns the corresponding clusterClaim object, and an error if there is any.
|
||||
func (c *FakeClusterClaims) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterClaim, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootGetAction(clusterclaimsResource, name), &v1alpha1.ClusterClaim{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.ClusterClaim), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ClusterClaims that match those selectors.
|
||||
func (c *FakeClusterClaims) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterClaimList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootListAction(clusterclaimsResource, clusterclaimsKind, opts), &v1alpha1.ClusterClaimList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1alpha1.ClusterClaimList{ListMeta: obj.(*v1alpha1.ClusterClaimList).ListMeta}
|
||||
for _, item := range obj.(*v1alpha1.ClusterClaimList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterClaims.
|
||||
func (c *FakeClusterClaims) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewRootWatchAction(clusterclaimsResource, opts))
|
||||
}
|
||||
|
||||
// Create takes the representation of a clusterClaim and creates it. Returns the server's representation of the clusterClaim, and an error, if there is any.
|
||||
func (c *FakeClusterClaims) Create(ctx context.Context, clusterClaim *v1alpha1.ClusterClaim, opts v1.CreateOptions) (result *v1alpha1.ClusterClaim, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootCreateAction(clusterclaimsResource, clusterClaim), &v1alpha1.ClusterClaim{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.ClusterClaim), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a clusterClaim and updates it. Returns the server's representation of the clusterClaim, and an error, if there is any.
|
||||
func (c *FakeClusterClaims) Update(ctx context.Context, clusterClaim *v1alpha1.ClusterClaim, opts v1.UpdateOptions) (result *v1alpha1.ClusterClaim, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateAction(clusterclaimsResource, clusterClaim), &v1alpha1.ClusterClaim{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.ClusterClaim), err
|
||||
}
|
||||
|
||||
// Delete takes name of the clusterClaim and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeClusterClaims) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewRootDeleteActionWithOptions(clusterclaimsResource, name, opts), &v1alpha1.ClusterClaim{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeClusterClaims) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewRootDeleteCollectionAction(clusterclaimsResource, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1alpha1.ClusterClaimList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched clusterClaim.
|
||||
func (c *FakeClusterClaims) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterClaim, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootPatchSubresourceAction(clusterclaimsResource, name, pt, data, subresources...), &v1alpha1.ClusterClaim{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.ClusterClaim), err
|
||||
}
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
rest "k8s.io/client-go/rest"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta1 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta1"
|
||||
)
|
||||
|
||||
type FakeClusterV1beta1 struct {
|
||||
*testing.Fake
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1beta1) ManagedClusterSets() v1beta1.ManagedClusterSetInterface {
|
||||
return &FakeManagedClusterSets{c}
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1beta1) ManagedClusterSetBindings(namespace string) v1beta1.ManagedClusterSetBindingInterface {
|
||||
return &FakeManagedClusterSetBindings{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1beta1) Placements(namespace string) v1beta1.PlacementInterface {
|
||||
return &FakePlacements{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1beta1) PlacementDecisions(namespace string) v1beta1.PlacementDecisionInterface {
|
||||
return &FakePlacementDecisions{c, namespace}
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeClusterV1beta1) RESTClient() rest.Interface {
|
||||
var ret *rest.RESTClient
|
||||
return ret
|
||||
}
|
||||
Generated
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
)
|
||||
|
||||
// FakeManagedClusterSets implements ManagedClusterSetInterface
|
||||
type FakeManagedClusterSets struct {
|
||||
Fake *FakeClusterV1beta1
|
||||
}
|
||||
|
||||
var managedclustersetsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Resource: "managedclustersets"}
|
||||
|
||||
var managedclustersetsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Kind: "ManagedClusterSet"}
|
||||
|
||||
// Get takes name of the managedClusterSet, and returns the corresponding managedClusterSet object, and an error if there is any.
|
||||
func (c *FakeManagedClusterSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootGetAction(managedclustersetsResource, name), &v1beta1.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ManagedClusterSets that match those selectors.
|
||||
func (c *FakeManagedClusterSets) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.ManagedClusterSetList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootListAction(managedclustersetsResource, managedclustersetsKind, opts), &v1beta1.ManagedClusterSetList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta1.ManagedClusterSetList{ListMeta: obj.(*v1beta1.ManagedClusterSetList).ListMeta}
|
||||
for _, item := range obj.(*v1beta1.ManagedClusterSetList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested managedClusterSets.
|
||||
func (c *FakeManagedClusterSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewRootWatchAction(managedclustersetsResource, opts))
|
||||
}
|
||||
|
||||
// Create takes the representation of a managedClusterSet and creates it. Returns the server's representation of the managedClusterSet, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSets) Create(ctx context.Context, managedClusterSet *v1beta1.ManagedClusterSet, opts v1.CreateOptions) (result *v1beta1.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootCreateAction(managedclustersetsResource, managedClusterSet), &v1beta1.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a managedClusterSet and updates it. Returns the server's representation of the managedClusterSet, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSets) Update(ctx context.Context, managedClusterSet *v1beta1.ManagedClusterSet, opts v1.UpdateOptions) (result *v1beta1.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateAction(managedclustersetsResource, managedClusterSet), &v1beta1.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeManagedClusterSets) UpdateStatus(ctx context.Context, managedClusterSet *v1beta1.ManagedClusterSet, opts v1.UpdateOptions) (*v1beta1.ManagedClusterSet, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateSubresourceAction(managedclustersetsResource, "status", managedClusterSet), &v1beta1.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// Delete takes name of the managedClusterSet and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeManagedClusterSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewRootDeleteActionWithOptions(managedclustersetsResource, name, opts), &v1beta1.ManagedClusterSet{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeManagedClusterSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewRootDeleteCollectionAction(managedclustersetsResource, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta1.ManagedClusterSetList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched managedClusterSet.
|
||||
func (c *FakeManagedClusterSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootPatchSubresourceAction(managedclustersetsResource, name, pt, data, subresources...), &v1beta1.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSet), err
|
||||
}
|
||||
Generated
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
)
|
||||
|
||||
// FakeManagedClusterSetBindings implements ManagedClusterSetBindingInterface
|
||||
type FakeManagedClusterSetBindings struct {
|
||||
Fake *FakeClusterV1beta1
|
||||
ns string
|
||||
}
|
||||
|
||||
var managedclustersetbindingsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Resource: "managedclustersetbindings"}
|
||||
|
||||
var managedclustersetbindingsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Kind: "ManagedClusterSetBinding"}
|
||||
|
||||
// Get takes name of the managedClusterSetBinding, and returns the corresponding managedClusterSetBinding object, and an error if there is any.
|
||||
func (c *FakeManagedClusterSetBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(managedclustersetbindingsResource, c.ns, name), &v1beta1.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ManagedClusterSetBindings that match those selectors.
|
||||
func (c *FakeManagedClusterSetBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.ManagedClusterSetBindingList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(managedclustersetbindingsResource, managedclustersetbindingsKind, c.ns, opts), &v1beta1.ManagedClusterSetBindingList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta1.ManagedClusterSetBindingList{ListMeta: obj.(*v1beta1.ManagedClusterSetBindingList).ListMeta}
|
||||
for _, item := range obj.(*v1beta1.ManagedClusterSetBindingList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested managedClusterSetBindings.
|
||||
func (c *FakeManagedClusterSetBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(managedclustersetbindingsResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a managedClusterSetBinding and creates it. Returns the server's representation of the managedClusterSetBinding, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSetBindings) Create(ctx context.Context, managedClusterSetBinding *v1beta1.ManagedClusterSetBinding, opts v1.CreateOptions) (result *v1beta1.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(managedclustersetbindingsResource, c.ns, managedClusterSetBinding), &v1beta1.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a managedClusterSetBinding and updates it. Returns the server's representation of the managedClusterSetBinding, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSetBindings) Update(ctx context.Context, managedClusterSetBinding *v1beta1.ManagedClusterSetBinding, opts v1.UpdateOptions) (result *v1beta1.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(managedclustersetbindingsResource, c.ns, managedClusterSetBinding), &v1beta1.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeManagedClusterSetBindings) UpdateStatus(ctx context.Context, managedClusterSetBinding *v1beta1.ManagedClusterSetBinding, opts v1.UpdateOptions) (*v1beta1.ManagedClusterSetBinding, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(managedclustersetbindingsResource, "status", c.ns, managedClusterSetBinding), &v1beta1.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// Delete takes name of the managedClusterSetBinding and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeManagedClusterSetBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteActionWithOptions(managedclustersetbindingsResource, c.ns, name, opts), &v1beta1.ManagedClusterSetBinding{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeManagedClusterSetBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(managedclustersetbindingsResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta1.ManagedClusterSetBindingList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched managedClusterSetBinding.
|
||||
func (c *FakeManagedClusterSetBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(managedclustersetbindingsResource, c.ns, name, pt, data, subresources...), &v1beta1.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ManagedClusterSetBinding), err
|
||||
}
|
||||
Generated
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
)
|
||||
|
||||
// FakePlacements implements PlacementInterface
|
||||
type FakePlacements struct {
|
||||
Fake *FakeClusterV1beta1
|
||||
ns string
|
||||
}
|
||||
|
||||
var placementsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Resource: "placements"}
|
||||
|
||||
var placementsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Kind: "Placement"}
|
||||
|
||||
// Get takes name of the placement, and returns the corresponding placement object, and an error if there is any.
|
||||
func (c *FakePlacements) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Placement, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(placementsResource, c.ns, name), &v1beta1.Placement{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.Placement), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of Placements that match those selectors.
|
||||
func (c *FakePlacements) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.PlacementList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(placementsResource, placementsKind, c.ns, opts), &v1beta1.PlacementList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta1.PlacementList{ListMeta: obj.(*v1beta1.PlacementList).ListMeta}
|
||||
for _, item := range obj.(*v1beta1.PlacementList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested placements.
|
||||
func (c *FakePlacements) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(placementsResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a placement and creates it. Returns the server's representation of the placement, and an error, if there is any.
|
||||
func (c *FakePlacements) Create(ctx context.Context, placement *v1beta1.Placement, opts v1.CreateOptions) (result *v1beta1.Placement, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(placementsResource, c.ns, placement), &v1beta1.Placement{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.Placement), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a placement and updates it. Returns the server's representation of the placement, and an error, if there is any.
|
||||
func (c *FakePlacements) Update(ctx context.Context, placement *v1beta1.Placement, opts v1.UpdateOptions) (result *v1beta1.Placement, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(placementsResource, c.ns, placement), &v1beta1.Placement{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.Placement), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakePlacements) UpdateStatus(ctx context.Context, placement *v1beta1.Placement, opts v1.UpdateOptions) (*v1beta1.Placement, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(placementsResource, "status", c.ns, placement), &v1beta1.Placement{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.Placement), err
|
||||
}
|
||||
|
||||
// Delete takes name of the placement and deletes it. Returns an error if one occurs.
|
||||
func (c *FakePlacements) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteActionWithOptions(placementsResource, c.ns, name, opts), &v1beta1.Placement{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakePlacements) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(placementsResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta1.PlacementList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched placement.
|
||||
func (c *FakePlacements) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Placement, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(placementsResource, c.ns, name, pt, data, subresources...), &v1beta1.Placement{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.Placement), err
|
||||
}
|
||||
Generated
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta1 "open-cluster-management.io/api/cluster/v1beta1"
|
||||
)
|
||||
|
||||
// FakePlacementDecisions implements PlacementDecisionInterface
|
||||
type FakePlacementDecisions struct {
|
||||
Fake *FakeClusterV1beta1
|
||||
ns string
|
||||
}
|
||||
|
||||
var placementdecisionsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Resource: "placementdecisions"}
|
||||
|
||||
var placementdecisionsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1beta1", Kind: "PlacementDecision"}
|
||||
|
||||
// Get takes name of the placementDecision, and returns the corresponding placementDecision object, and an error if there is any.
|
||||
func (c *FakePlacementDecisions) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.PlacementDecision, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(placementdecisionsResource, c.ns, name), &v1beta1.PlacementDecision{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.PlacementDecision), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of PlacementDecisions that match those selectors.
|
||||
func (c *FakePlacementDecisions) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.PlacementDecisionList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(placementdecisionsResource, placementdecisionsKind, c.ns, opts), &v1beta1.PlacementDecisionList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta1.PlacementDecisionList{ListMeta: obj.(*v1beta1.PlacementDecisionList).ListMeta}
|
||||
for _, item := range obj.(*v1beta1.PlacementDecisionList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested placementDecisions.
|
||||
func (c *FakePlacementDecisions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(placementdecisionsResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a placementDecision and creates it. Returns the server's representation of the placementDecision, and an error, if there is any.
|
||||
func (c *FakePlacementDecisions) Create(ctx context.Context, placementDecision *v1beta1.PlacementDecision, opts v1.CreateOptions) (result *v1beta1.PlacementDecision, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(placementdecisionsResource, c.ns, placementDecision), &v1beta1.PlacementDecision{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.PlacementDecision), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a placementDecision and updates it. Returns the server's representation of the placementDecision, and an error, if there is any.
|
||||
func (c *FakePlacementDecisions) Update(ctx context.Context, placementDecision *v1beta1.PlacementDecision, opts v1.UpdateOptions) (result *v1beta1.PlacementDecision, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(placementdecisionsResource, c.ns, placementDecision), &v1beta1.PlacementDecision{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.PlacementDecision), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakePlacementDecisions) UpdateStatus(ctx context.Context, placementDecision *v1beta1.PlacementDecision, opts v1.UpdateOptions) (*v1beta1.PlacementDecision, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(placementdecisionsResource, "status", c.ns, placementDecision), &v1beta1.PlacementDecision{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.PlacementDecision), err
|
||||
}
|
||||
|
||||
// Delete takes name of the placementDecision and deletes it. Returns an error if one occurs.
|
||||
func (c *FakePlacementDecisions) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteActionWithOptions(placementdecisionsResource, c.ns, name, opts), &v1beta1.PlacementDecision{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakePlacementDecisions) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(placementdecisionsResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta1.PlacementDecisionList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched placementDecision.
|
||||
func (c *FakePlacementDecisions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.PlacementDecision, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(placementdecisionsResource, c.ns, name, pt, data, subresources...), &v1beta1.PlacementDecision{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.PlacementDecision), err
|
||||
}
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake
|
||||
Generated
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
rest "k8s.io/client-go/rest"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta2 "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1beta2"
|
||||
)
|
||||
|
||||
type FakeClusterV1beta2 struct {
|
||||
*testing.Fake
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1beta2) ManagedClusterSets() v1beta2.ManagedClusterSetInterface {
|
||||
return &FakeManagedClusterSets{c}
|
||||
}
|
||||
|
||||
func (c *FakeClusterV1beta2) ManagedClusterSetBindings(namespace string) v1beta2.ManagedClusterSetBindingInterface {
|
||||
return &FakeManagedClusterSetBindings{c, namespace}
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeClusterV1beta2) RESTClient() rest.Interface {
|
||||
var ret *rest.RESTClient
|
||||
return ret
|
||||
}
|
||||
Generated
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta2 "open-cluster-management.io/api/cluster/v1beta2"
|
||||
)
|
||||
|
||||
// FakeManagedClusterSets implements ManagedClusterSetInterface
|
||||
type FakeManagedClusterSets struct {
|
||||
Fake *FakeClusterV1beta2
|
||||
}
|
||||
|
||||
var managedclustersetsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1beta2", Resource: "managedclustersets"}
|
||||
|
||||
var managedclustersetsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1beta2", Kind: "ManagedClusterSet"}
|
||||
|
||||
// Get takes name of the managedClusterSet, and returns the corresponding managedClusterSet object, and an error if there is any.
|
||||
func (c *FakeManagedClusterSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootGetAction(managedclustersetsResource, name), &v1beta2.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ManagedClusterSets that match those selectors.
|
||||
func (c *FakeManagedClusterSets) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.ManagedClusterSetList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootListAction(managedclustersetsResource, managedclustersetsKind, opts), &v1beta2.ManagedClusterSetList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta2.ManagedClusterSetList{ListMeta: obj.(*v1beta2.ManagedClusterSetList).ListMeta}
|
||||
for _, item := range obj.(*v1beta2.ManagedClusterSetList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested managedClusterSets.
|
||||
func (c *FakeManagedClusterSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewRootWatchAction(managedclustersetsResource, opts))
|
||||
}
|
||||
|
||||
// Create takes the representation of a managedClusterSet and creates it. Returns the server's representation of the managedClusterSet, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSets) Create(ctx context.Context, managedClusterSet *v1beta2.ManagedClusterSet, opts v1.CreateOptions) (result *v1beta2.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootCreateAction(managedclustersetsResource, managedClusterSet), &v1beta2.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a managedClusterSet and updates it. Returns the server's representation of the managedClusterSet, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSets) Update(ctx context.Context, managedClusterSet *v1beta2.ManagedClusterSet, opts v1.UpdateOptions) (result *v1beta2.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateAction(managedclustersetsResource, managedClusterSet), &v1beta2.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeManagedClusterSets) UpdateStatus(ctx context.Context, managedClusterSet *v1beta2.ManagedClusterSet, opts v1.UpdateOptions) (*v1beta2.ManagedClusterSet, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootUpdateSubresourceAction(managedclustersetsResource, "status", managedClusterSet), &v1beta2.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSet), err
|
||||
}
|
||||
|
||||
// Delete takes name of the managedClusterSet and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeManagedClusterSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewRootDeleteActionWithOptions(managedclustersetsResource, name, opts), &v1beta2.ManagedClusterSet{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeManagedClusterSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewRootDeleteCollectionAction(managedclustersetsResource, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta2.ManagedClusterSetList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched managedClusterSet.
|
||||
func (c *FakeManagedClusterSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.ManagedClusterSet, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewRootPatchSubresourceAction(managedclustersetsResource, name, pt, data, subresources...), &v1beta2.ManagedClusterSet{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSet), err
|
||||
}
|
||||
Generated
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
v1beta2 "open-cluster-management.io/api/cluster/v1beta2"
|
||||
)
|
||||
|
||||
// FakeManagedClusterSetBindings implements ManagedClusterSetBindingInterface
|
||||
type FakeManagedClusterSetBindings struct {
|
||||
Fake *FakeClusterV1beta2
|
||||
ns string
|
||||
}
|
||||
|
||||
var managedclustersetbindingsResource = schema.GroupVersionResource{Group: "cluster.open-cluster-management.io", Version: "v1beta2", Resource: "managedclustersetbindings"}
|
||||
|
||||
var managedclustersetbindingsKind = schema.GroupVersionKind{Group: "cluster.open-cluster-management.io", Version: "v1beta2", Kind: "ManagedClusterSetBinding"}
|
||||
|
||||
// Get takes name of the managedClusterSetBinding, and returns the corresponding managedClusterSetBinding object, and an error if there is any.
|
||||
func (c *FakeManagedClusterSetBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(managedclustersetbindingsResource, c.ns, name), &v1beta2.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ManagedClusterSetBindings that match those selectors.
|
||||
func (c *FakeManagedClusterSetBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.ManagedClusterSetBindingList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(managedclustersetbindingsResource, managedclustersetbindingsKind, c.ns, opts), &v1beta2.ManagedClusterSetBindingList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta2.ManagedClusterSetBindingList{ListMeta: obj.(*v1beta2.ManagedClusterSetBindingList).ListMeta}
|
||||
for _, item := range obj.(*v1beta2.ManagedClusterSetBindingList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested managedClusterSetBindings.
|
||||
func (c *FakeManagedClusterSetBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(managedclustersetbindingsResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a managedClusterSetBinding and creates it. Returns the server's representation of the managedClusterSetBinding, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSetBindings) Create(ctx context.Context, managedClusterSetBinding *v1beta2.ManagedClusterSetBinding, opts v1.CreateOptions) (result *v1beta2.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(managedclustersetbindingsResource, c.ns, managedClusterSetBinding), &v1beta2.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a managedClusterSetBinding and updates it. Returns the server's representation of the managedClusterSetBinding, and an error, if there is any.
|
||||
func (c *FakeManagedClusterSetBindings) Update(ctx context.Context, managedClusterSetBinding *v1beta2.ManagedClusterSetBinding, opts v1.UpdateOptions) (result *v1beta2.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(managedclustersetbindingsResource, c.ns, managedClusterSetBinding), &v1beta2.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeManagedClusterSetBindings) UpdateStatus(ctx context.Context, managedClusterSetBinding *v1beta2.ManagedClusterSetBinding, opts v1.UpdateOptions) (*v1beta2.ManagedClusterSetBinding, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(managedclustersetbindingsResource, "status", c.ns, managedClusterSetBinding), &v1beta2.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSetBinding), err
|
||||
}
|
||||
|
||||
// Delete takes name of the managedClusterSetBinding and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeManagedClusterSetBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteActionWithOptions(managedclustersetbindingsResource, c.ns, name, opts), &v1beta2.ManagedClusterSetBinding{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeManagedClusterSetBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(managedclustersetbindingsResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta2.ManagedClusterSetBindingList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched managedClusterSetBinding.
|
||||
func (c *FakeManagedClusterSetBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.ManagedClusterSetBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(managedclustersetbindingsResource, c.ns, name, pt, data, subresources...), &v1beta2.ManagedClusterSetBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.ManagedClusterSetBinding), err
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package workapplier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/klog/v2"
|
||||
workv1client "open-cluster-management.io/api/client/work/clientset/versioned"
|
||||
worklister "open-cluster-management.io/api/client/work/listers/work/v1"
|
||||
workapiv1 "open-cluster-management.io/api/work/v1"
|
||||
)
|
||||
|
||||
type WorkApplier struct {
|
||||
cache *workCache
|
||||
getWork func(ctx context.Context, namespace, name string) (*workapiv1.ManifestWork, error)
|
||||
deleteWork func(ctx context.Context, namespace, name string) error
|
||||
patchWork func(ctx context.Context, namespace, name string, pt types.PatchType, data []byte) (*workapiv1.ManifestWork, error)
|
||||
createWork func(ctx context.Context, work *workapiv1.ManifestWork) (*workapiv1.ManifestWork, error)
|
||||
}
|
||||
|
||||
func NewWorkApplierWithTypedClient(workClient workv1client.Interface,
|
||||
workLister worklister.ManifestWorkLister) *WorkApplier {
|
||||
return &WorkApplier{
|
||||
cache: newWorkCache(),
|
||||
getWork: func(ctx context.Context, namespace, name string) (*workapiv1.ManifestWork, error) {
|
||||
return workLister.ManifestWorks(namespace).Get(name)
|
||||
},
|
||||
deleteWork: func(ctx context.Context, namespace, name string) error {
|
||||
return workClient.WorkV1().ManifestWorks(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
},
|
||||
createWork: func(ctx context.Context, work *workapiv1.ManifestWork) (*workapiv1.ManifestWork, error) {
|
||||
return workClient.WorkV1().ManifestWorks(work.Namespace).Create(ctx, work, metav1.CreateOptions{})
|
||||
},
|
||||
patchWork: func(ctx context.Context, namespace, name string, pt types.PatchType, data []byte) (*workapiv1.ManifestWork, error) {
|
||||
return workClient.WorkV1().ManifestWorks(namespace).Patch(ctx, name, pt, data, metav1.PatchOptions{})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WorkApplier) Apply(ctx context.Context, work *workapiv1.ManifestWork) (*workapiv1.ManifestWork, error) {
|
||||
existingWork, err := w.getWork(ctx, work.Namespace, work.Name)
|
||||
existingWork = existingWork.DeepCopy()
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
existingWork, err = w.createWork(ctx, work)
|
||||
if err == nil {
|
||||
w.cache.updateCache(work, existingWork)
|
||||
return existingWork, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if w.cache.safeToSkipApply(work, existingWork) {
|
||||
return existingWork, nil
|
||||
}
|
||||
|
||||
if ManifestWorkSpecEqual(work.Spec, existingWork.Spec) {
|
||||
return existingWork, nil
|
||||
}
|
||||
|
||||
oldData, err := json.Marshal(&workapiv1.ManifestWork{
|
||||
Spec: existingWork.Spec,
|
||||
})
|
||||
if err != nil {
|
||||
return existingWork, err
|
||||
}
|
||||
|
||||
newData, err := json.Marshal(&workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: existingWork.UID,
|
||||
ResourceVersion: existingWork.ResourceVersion,
|
||||
},
|
||||
Spec: work.Spec,
|
||||
})
|
||||
if err != nil {
|
||||
return existingWork, err
|
||||
}
|
||||
|
||||
patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
|
||||
if err != nil {
|
||||
return existingWork, fmt.Errorf("failed to create patch for addon %s: %w", existingWork.Name, err)
|
||||
}
|
||||
|
||||
klog.V(2).Infof("Patching work %s/%s with %s", existingWork.Namespace, existingWork.Name, string(patchBytes))
|
||||
updated, err := w.patchWork(ctx, existingWork.Namespace, existingWork.Name, types.MergePatchType, patchBytes)
|
||||
if err == nil {
|
||||
w.cache.updateCache(work, existingWork)
|
||||
return updated, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (w *WorkApplier) Delete(ctx context.Context, namespace, name string) error {
|
||||
err := w.deleteWork(ctx, namespace, name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
w.cache.removeCache(name, namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
func manifestsEqual(new, old []workapiv1.Manifest) bool {
|
||||
if len(new) != len(old) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range new {
|
||||
if !equality.Semantic.DeepEqual(new[i].Raw, old[i].Raw) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ManifestWorkSpecEqual(new, old workapiv1.ManifestWorkSpec) bool {
|
||||
if !manifestsEqual(new.Workload.Manifests, old.Workload.Manifests) {
|
||||
return false
|
||||
}
|
||||
if !equality.Semantic.DeepEqual(new.ManifestConfigs, old.ManifestConfigs) {
|
||||
return false
|
||||
}
|
||||
if !equality.Semantic.DeepEqual(new.DeleteOption, old.DeleteOption) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package workapplier
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
workapiv1 "open-cluster-management.io/api/work/v1"
|
||||
)
|
||||
|
||||
type workKey struct {
|
||||
cluster string
|
||||
name string
|
||||
}
|
||||
|
||||
type cachedResource struct {
|
||||
resourceHash string
|
||||
generation int64
|
||||
}
|
||||
|
||||
type workCache struct {
|
||||
mutex sync.RWMutex
|
||||
cache map[workKey]cachedResource
|
||||
}
|
||||
|
||||
func newWorkCache() *workCache {
|
||||
return &workCache{
|
||||
cache: map[workKey]cachedResource{},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *workCache) updateCache(required, existing *workapiv1.ManifestWork) {
|
||||
if required == nil || existing == nil {
|
||||
return
|
||||
}
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
key := workKey{
|
||||
cluster: required.Namespace,
|
||||
name: required.Name,
|
||||
}
|
||||
|
||||
value := cachedResource{
|
||||
resourceHash: hashOfResourceStruct(required),
|
||||
generation: existing.Generation,
|
||||
}
|
||||
|
||||
w.cache[key] = value
|
||||
}
|
||||
|
||||
func (w *workCache) removeCache(name, cluster string) {
|
||||
key := workKey{
|
||||
cluster: cluster,
|
||||
name: name,
|
||||
}
|
||||
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
delete(w.cache, key)
|
||||
}
|
||||
|
||||
func (w *workCache) safeToSkipApply(required, existing *workapiv1.ManifestWork) bool {
|
||||
if required == nil || existing == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
cacheKey := workKey{
|
||||
cluster: required.Namespace,
|
||||
name: required.Name,
|
||||
}
|
||||
|
||||
resourceHash := hashOfResourceStruct(required)
|
||||
|
||||
generation := existing.Generation
|
||||
w.mutex.RLock()
|
||||
defer w.mutex.RUnlock()
|
||||
var generationMatch, hashMatch bool
|
||||
if cached, exists := w.cache[cacheKey]; exists {
|
||||
generationMatch = cached.generation == generation
|
||||
hashMatch = cached.resourceHash == resourceHash
|
||||
if generationMatch && hashMatch {
|
||||
klog.V(4).Infof("found matching generation & manifest hash")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func hashOfResourceStruct(o interface{}) string {
|
||||
oString, _ := json.Marshal(o)
|
||||
h := md5.New()
|
||||
if _, err := io.WriteString(h, string(oString)); err != nil {
|
||||
klog.Errorf("failed to hash object: %v", err)
|
||||
}
|
||||
rval := fmt.Sprintf("%x", h.Sum(nil))
|
||||
return rval
|
||||
}
|
||||
Reference in New Issue
Block a user