github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/testing/fake.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package testing
    21  
    22  import (
    23  	"fmt"
    24  	"time"
    25  
    26  	chaosmeshv1alpha1 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
    27  	snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
    28  	"github.com/sethvargo/go-password/password"
    29  	appsv1 "k8s.io/api/apps/v1"
    30  	batchv1 "k8s.io/api/batch/v1"
    31  	corev1 "k8s.io/api/core/v1"
    32  	rbacv1 "k8s.io/api/rbac/v1"
    33  	storagev1 "k8s.io/api/storage/v1"
    34  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    35  	"k8s.io/apimachinery/pkg/api/resource"
    36  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    37  	"k8s.io/apimachinery/pkg/runtime/schema"
    38  	"k8s.io/kubectl/pkg/util/storage"
    39  	"k8s.io/utils/pointer"
    40  
    41  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    42  	dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1"
    43  	extensionsv1alpha1 "github.com/1aal/kubeblocks/apis/extensions/v1alpha1"
    44  	storagev1alpha1 "github.com/1aal/kubeblocks/apis/storage/v1alpha1"
    45  	"github.com/1aal/kubeblocks/pkg/cli/types"
    46  	"github.com/1aal/kubeblocks/pkg/constant"
    47  	dptypes "github.com/1aal/kubeblocks/pkg/dataprotection/types"
    48  	"github.com/1aal/kubeblocks/pkg/dataprotection/utils/boolptr"
    49  	testapps "github.com/1aal/kubeblocks/pkg/testutil/apps"
    50  )
    51  
    52  const (
    53  	ClusterName        = "fake-cluster-name"
    54  	Namespace          = "fake-namespace"
    55  	ClusterVersionName = "fake-cluster-version"
    56  	ClusterDefName     = "fake-cluster-definition"
    57  	ComponentName      = "fake-component-name"
    58  	ComponentDefName   = "fake-component-type"
    59  	NodeName           = "fake-node-name"
    60  	SecretName         = "fake-secret-conn-credential"
    61  	StorageClassName   = "fake-storage-class"
    62  	PVCName            = "fake-pvc"
    63  	ServiceRefName     = "fake-serviceRef"
    64  
    65  	KubeBlocksRepoName  = "fake-kubeblocks-repo"
    66  	KubeBlocksChartName = "fake-kubeblocks"
    67  	KubeBlocksChartURL  = "fake-kubeblocks-chart-url"
    68  	BackupMethodName    = "fake-backup-method"
    69  	ActionSetName       = "fake-action-set"
    70  	BackupName          = "fake-backup-name"
    71  
    72  	IsDefault    = true
    73  	IsNotDefault = false
    74  )
    75  
    76  var (
    77  	ExtraComponentDefName = fmt.Sprintf("%s-%d", ComponentDefName, 1)
    78  )
    79  
    80  func GetRandomStr() string {
    81  	seq, _ := password.Generate(6, 2, 0, true, true)
    82  	return seq
    83  }
    84  
    85  func FakeCluster(name, namespace string, conditions ...metav1.Condition) *appsv1alpha1.Cluster {
    86  	var replicas int32 = 1
    87  
    88  	return &appsv1alpha1.Cluster{
    89  		TypeMeta: metav1.TypeMeta{
    90  			Kind:       types.KindCluster,
    91  			APIVersion: fmt.Sprintf("%s/%s", types.AppsAPIGroup, types.AppsAPIVersion),
    92  		},
    93  		ObjectMeta: metav1.ObjectMeta{
    94  			Name:      name,
    95  			Namespace: namespace,
    96  			UID:       "b262b889-a27f-42d8-b066-2978561c8167",
    97  		},
    98  		Status: appsv1alpha1.ClusterStatus{
    99  			Phase: appsv1alpha1.RunningClusterPhase,
   100  			Components: map[string]appsv1alpha1.ClusterComponentStatus{
   101  				ComponentName: {
   102  					ConsensusSetStatus: &appsv1alpha1.ConsensusSetStatus{
   103  						Leader: appsv1alpha1.ConsensusMemberStatus{
   104  							Name:       "leader",
   105  							AccessMode: appsv1alpha1.ReadWrite,
   106  							Pod:        fmt.Sprintf("%s-pod-0", name),
   107  						},
   108  					},
   109  				},
   110  			},
   111  			Conditions: conditions,
   112  		},
   113  		Spec: appsv1alpha1.ClusterSpec{
   114  			ClusterDefRef:     ClusterDefName,
   115  			ClusterVersionRef: ClusterVersionName,
   116  			TerminationPolicy: appsv1alpha1.WipeOut,
   117  			ComponentSpecs: []appsv1alpha1.ClusterComponentSpec{
   118  				{
   119  					Name:            ComponentName,
   120  					ComponentDefRef: ComponentDefName,
   121  					Replicas:        replicas,
   122  					Resources: corev1.ResourceRequirements{
   123  						Requests: corev1.ResourceList{
   124  							corev1.ResourceCPU:    resource.MustParse("100m"),
   125  							corev1.ResourceMemory: resource.MustParse("100Mi"),
   126  						},
   127  						Limits: corev1.ResourceList{
   128  							corev1.ResourceCPU:    resource.MustParse("200m"),
   129  							corev1.ResourceMemory: resource.MustParse("2Gi"),
   130  						},
   131  					},
   132  					VolumeClaimTemplates: []appsv1alpha1.ClusterComponentVolumeClaimTemplate{
   133  						{
   134  							Name: "data",
   135  							Spec: appsv1alpha1.PersistentVolumeClaimSpec{
   136  								AccessModes: []corev1.PersistentVolumeAccessMode{
   137  									corev1.ReadWriteOnce,
   138  								},
   139  								Resources: corev1.ResourceRequirements{
   140  									Requests: corev1.ResourceList{
   141  										corev1.ResourceStorage: resource.MustParse("1Gi"),
   142  									},
   143  								},
   144  							},
   145  						},
   146  					},
   147  				},
   148  				{
   149  					Name:            ComponentName + "-1",
   150  					ComponentDefRef: ComponentDefName,
   151  					Replicas:        replicas,
   152  					Resources: corev1.ResourceRequirements{
   153  						Requests: corev1.ResourceList{
   154  							corev1.ResourceCPU:    resource.MustParse("100m"),
   155  							corev1.ResourceMemory: resource.MustParse("100Mi"),
   156  						},
   157  					},
   158  					VolumeClaimTemplates: []appsv1alpha1.ClusterComponentVolumeClaimTemplate{
   159  						{
   160  							Name: "data",
   161  							Spec: appsv1alpha1.PersistentVolumeClaimSpec{
   162  								AccessModes: []corev1.PersistentVolumeAccessMode{
   163  									corev1.ReadWriteOnce,
   164  								},
   165  								Resources: corev1.ResourceRequirements{
   166  									Requests: corev1.ResourceList{
   167  										corev1.ResourceStorage: resource.MustParse("1Gi"),
   168  									},
   169  								},
   170  							},
   171  						},
   172  					},
   173  				},
   174  			},
   175  		},
   176  	}
   177  }
   178  
   179  func FakePods(replicas int, namespace string, cluster string) *corev1.PodList {
   180  	pods := &corev1.PodList{}
   181  	for i := 0; i < replicas; i++ {
   182  		role := "follower"
   183  		pod := corev1.Pod{}
   184  		pod.Name = fmt.Sprintf("%s-pod-%d", cluster, i)
   185  		pod.Namespace = namespace
   186  
   187  		if i == 0 {
   188  			role = "leader"
   189  		}
   190  
   191  		pod.Labels = map[string]string{
   192  			constant.AppInstanceLabelKey:    cluster,
   193  			constant.RoleLabelKey:           role,
   194  			constant.KBAppComponentLabelKey: ComponentName,
   195  			constant.AppNameLabelKey:        "mysql-apecloud-mysql",
   196  			constant.AppManagedByLabelKey:   constant.AppName,
   197  		}
   198  		pod.Spec.NodeName = NodeName
   199  		pod.Spec.Containers = []corev1.Container{
   200  			{
   201  				Name:  "fake-container",
   202  				Image: "fake-container-image",
   203  			},
   204  		}
   205  		pod.Status.Phase = corev1.PodRunning
   206  		pods.Items = append(pods.Items, pod)
   207  	}
   208  	return pods
   209  }
   210  
   211  // FakeSecret for test cluster create
   212  func FakeSecret(namespace string, cluster string) *corev1.Secret {
   213  	secret := corev1.Secret{}
   214  	secret.Name = SecretName
   215  	secret.Namespace = namespace
   216  	secret.Type = corev1.SecretTypeServiceAccountToken
   217  	secret.Labels = map[string]string{
   218  		constant.AppInstanceLabelKey: cluster,
   219  		"name":                       types.KubeBlocksChartName,
   220  		"owner":                      "helm",
   221  	}
   222  
   223  	secret.Data = map[string][]byte{
   224  		corev1.ServiceAccountTokenKey: []byte("fake-secret-token"),
   225  		"fake-secret-key":             []byte("fake-secret-value"),
   226  		"username":                    []byte("test-user"),
   227  		"password":                    []byte("test-password"),
   228  	}
   229  	return &secret
   230  }
   231  
   232  func FakeSecrets(namespace string, cluster string) *corev1.SecretList {
   233  	secret := corev1.Secret{}
   234  	secret.Name = SecretName
   235  	secret.Namespace = namespace
   236  	secret.Type = corev1.SecretTypeServiceAccountToken
   237  	secret.Labels = map[string]string{
   238  		constant.AppInstanceLabelKey:  cluster,
   239  		constant.AppManagedByLabelKey: constant.AppName,
   240  	}
   241  
   242  	secret.Data = map[string][]byte{
   243  		corev1.ServiceAccountTokenKey: []byte("fake-secret-token"),
   244  		"fake-secret-key":             []byte("fake-secret-value"),
   245  		"username":                    []byte("test-user"),
   246  		"password":                    []byte("test-password"),
   247  	}
   248  	return &corev1.SecretList{Items: []corev1.Secret{secret}}
   249  }
   250  
   251  func FakeSecretsWithLabels(namespace string, labels map[string]string) *corev1.SecretList {
   252  	secret := corev1.Secret{}
   253  	secret.Name = GetRandomStr()
   254  	secret.Namespace = namespace
   255  	secret.Labels = labels
   256  	secret.Data = map[string][]byte{
   257  		"username": []byte("test-user"),
   258  		"password": []byte("test-password"),
   259  	}
   260  	return &corev1.SecretList{Items: []corev1.Secret{secret}}
   261  }
   262  
   263  func FakeNode() *corev1.Node {
   264  	node := &corev1.Node{}
   265  	node.Name = NodeName
   266  	node.Labels = map[string]string{
   267  		constant.RegionLabelKey: "fake-node-region",
   268  		constant.ZoneLabelKey:   "fake-node-zone",
   269  	}
   270  	return node
   271  }
   272  
   273  func FakeClusterDef() *appsv1alpha1.ClusterDefinition {
   274  	clusterDef := &appsv1alpha1.ClusterDefinition{}
   275  	clusterDef.Name = ClusterDefName
   276  	clusterDef.Spec.ComponentDefs = []appsv1alpha1.ClusterComponentDefinition{
   277  		{
   278  			Name:          ComponentDefName,
   279  			CharacterType: "mysql",
   280  			SystemAccounts: &appsv1alpha1.SystemAccountSpec{
   281  				CmdExecutorConfig: &appsv1alpha1.CmdExecutorConfig{
   282  					CommandExecutorEnvItem: appsv1alpha1.CommandExecutorEnvItem{
   283  						Image: "",
   284  					},
   285  					CommandExecutorItem: appsv1alpha1.CommandExecutorItem{
   286  						Command: []string{"mysql"},
   287  						Args:    []string{"-h$(KB_ACCOUNT_ENDPOINT)", "-e $(KB_ACCOUNT_STATEMENT)"},
   288  					},
   289  				},
   290  				PasswordConfig: appsv1alpha1.PasswordConfig{},
   291  				Accounts:       []appsv1alpha1.SystemAccountConfig{},
   292  			},
   293  			ConfigSpecs: []appsv1alpha1.ComponentConfigSpec{
   294  				{
   295  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   296  						Name:        "mysql-consensusset-config",
   297  						TemplateRef: "mysql8.0-config-template",
   298  						Namespace:   Namespace,
   299  						VolumeName:  "mysql-config",
   300  					},
   301  					ConfigConstraintRef: "mysql8.0-config-constraints",
   302  				},
   303  			},
   304  			ServiceRefDeclarations: []appsv1alpha1.ServiceRefDeclaration{
   305  				FakeServiceRef(ServiceRefName),
   306  			},
   307  		},
   308  		{
   309  			Name:          ExtraComponentDefName,
   310  			CharacterType: "mysql",
   311  			ConfigSpecs: []appsv1alpha1.ComponentConfigSpec{
   312  				{
   313  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   314  						Name:        "mysql-consensusset-config",
   315  						TemplateRef: "mysql8.0-config-template",
   316  						Namespace:   Namespace,
   317  						VolumeName:  "mysql-config",
   318  					},
   319  					ConfigConstraintRef: "mysql8.0-config-constraints",
   320  				},
   321  			},
   322  			SwitchoverSpec: &appsv1alpha1.SwitchoverSpec{
   323  				WithCandidate: &appsv1alpha1.SwitchoverAction{
   324  					CmdExecutorConfig: &appsv1alpha1.CmdExecutorConfig{
   325  						CommandExecutorEnvItem: appsv1alpha1.CommandExecutorEnvItem{
   326  							Image: "",
   327  						},
   328  						CommandExecutorItem: appsv1alpha1.CommandExecutorItem{
   329  							Command: []string{"mysql"},
   330  							Args:    []string{"-h$(KB_CONSENSUS_LEADER_POD_FQDN)", "-e $(KB_SWITCHOVER_ACTION)"},
   331  						},
   332  					},
   333  				},
   334  			},
   335  		},
   336  	}
   337  	return clusterDef
   338  }
   339  
   340  func FakeComponentClassDef(name string, clusterDefRef string, componentDefRef string) *appsv1alpha1.ComponentClassDefinition {
   341  	testapps.NewComponentResourceConstraintFactory(testapps.DefaultResourceConstraintName).
   342  		AddConstraints(testapps.GeneralResourceConstraint).
   343  		GetObject()
   344  
   345  	componentClassDefinition := testapps.NewComponentClassDefinitionFactory(name, clusterDefRef, componentDefRef).
   346  		AddClasses([]appsv1alpha1.ComponentClass{testapps.Class1c1g, testapps.Class2c4g}).
   347  		GetObject()
   348  
   349  	return componentClassDefinition
   350  }
   351  
   352  func FakeClusterVersion() *appsv1alpha1.ClusterVersion {
   353  	cv := &appsv1alpha1.ClusterVersion{}
   354  	gvr := types.ClusterVersionGVR()
   355  	cv.TypeMeta.APIVersion = gvr.GroupVersion().String()
   356  	cv.TypeMeta.Kind = types.KindClusterVersion
   357  	cv.Name = ClusterVersionName
   358  	cv.SetLabels(map[string]string{
   359  		constant.ClusterDefLabelKey:   ClusterDefName,
   360  		constant.AppManagedByLabelKey: constant.AppName,
   361  	})
   362  	cv.Spec.ClusterDefinitionRef = ClusterDefName
   363  	cv.SetCreationTimestamp(metav1.Now())
   364  	return cv
   365  }
   366  
   367  func FakeActionSet() *dpv1alpha1.ActionSet {
   368  	as := &dpv1alpha1.ActionSet{}
   369  	as.Name = ActionSetName
   370  	return as
   371  }
   372  
   373  func FakeBackupPolicy(backupPolicyName, clusterName string) *dpv1alpha1.BackupPolicy {
   374  	template := &dpv1alpha1.BackupPolicy{
   375  		TypeMeta: metav1.TypeMeta{
   376  			APIVersion: fmt.Sprintf("%s/%s", types.DPAPIGroup, types.DPAPIVersion),
   377  			Kind:       types.KindBackupPolicy,
   378  		},
   379  		ObjectMeta: metav1.ObjectMeta{
   380  			Name:      backupPolicyName,
   381  			Namespace: Namespace,
   382  			Labels: map[string]string{
   383  				constant.AppInstanceLabelKey: clusterName,
   384  			},
   385  			Annotations: map[string]string{
   386  				dptypes.DefaultBackupPolicyAnnotationKey: "true",
   387  			},
   388  		},
   389  		Spec: dpv1alpha1.BackupPolicySpec{
   390  			BackupMethods: []dpv1alpha1.BackupMethod{
   391  				{
   392  					Name:            BackupMethodName,
   393  					SnapshotVolumes: boolptr.False(),
   394  					ActionSetName:   ActionSetName,
   395  				},
   396  			},
   397  			Target: &dpv1alpha1.BackupTarget{
   398  				PodSelector: &dpv1alpha1.PodSelector{
   399  					LabelSelector: &metav1.LabelSelector{
   400  						MatchLabels: map[string]string{
   401  							constant.AppInstanceLabelKey:    ClusterName,
   402  							constant.KBAppComponentLabelKey: ComponentName,
   403  							constant.AppManagedByLabelKey:   constant.AppName},
   404  					},
   405  				},
   406  			},
   407  		},
   408  		Status: dpv1alpha1.BackupPolicyStatus{
   409  			Phase: dpv1alpha1.AvailablePhase,
   410  		},
   411  	}
   412  	return template
   413  }
   414  
   415  func FakeBackup(backupName string) *dpv1alpha1.Backup {
   416  	backup := &dpv1alpha1.Backup{
   417  		TypeMeta: metav1.TypeMeta{
   418  			APIVersion: fmt.Sprintf("%s/%s", types.DPAPIGroup, types.DPAPIVersion),
   419  			Kind:       types.KindBackup,
   420  		},
   421  		ObjectMeta: metav1.ObjectMeta{
   422  			Name:      backupName,
   423  			Namespace: Namespace,
   424  		},
   425  	}
   426  	backup.SetCreationTimestamp(metav1.Now())
   427  	return backup
   428  }
   429  
   430  func FakeBackupSchedule(backupScheduleName, backupPolicyName string) *dpv1alpha1.BackupSchedule {
   431  	backupSchedule := &dpv1alpha1.BackupSchedule{
   432  		TypeMeta: metav1.TypeMeta{
   433  			APIVersion: fmt.Sprintf("%s/%s", types.DPAPIGroup, types.DPAPIVersion),
   434  			Kind:       types.KindBackupSchedule,
   435  		},
   436  		ObjectMeta: metav1.ObjectMeta{
   437  			Name:      backupScheduleName,
   438  			Namespace: Namespace,
   439  			Labels: map[string]string{
   440  				constant.AppInstanceLabelKey: ClusterName,
   441  			},
   442  		},
   443  		Spec: dpv1alpha1.BackupScheduleSpec{
   444  			BackupPolicyName: backupPolicyName,
   445  			Schedules: []dpv1alpha1.SchedulePolicy{
   446  				{
   447  					Enabled:         boolptr.True(),
   448  					CronExpression:  "0 0 * * *",
   449  					BackupMethod:    BackupMethodName,
   450  					RetentionPeriod: dpv1alpha1.RetentionPeriod("1d"),
   451  				},
   452  			},
   453  		},
   454  		Status: dpv1alpha1.BackupScheduleStatus{
   455  			Phase: dpv1alpha1.BackupSchedulePhaseAvailable,
   456  		},
   457  	}
   458  	return backupSchedule
   459  }
   460  
   461  func FakeBackupPolicyTemplate(backupPolicyTemplateName string, clusterDef string) *appsv1alpha1.BackupPolicyTemplate {
   462  	backupPolicyTemplate := &appsv1alpha1.BackupPolicyTemplate{
   463  		TypeMeta: metav1.TypeMeta{
   464  			APIVersion: fmt.Sprintf("%s/%s", types.AppsAPIGroup, types.AppsAPIVersion),
   465  			Kind:       types.KindBackupPolicyTemplate,
   466  		},
   467  		ObjectMeta: metav1.ObjectMeta{
   468  			Name:      backupPolicyTemplateName,
   469  			Namespace: Namespace,
   470  			Labels: map[string]string{
   471  				constant.ClusterDefLabelKey: ClusterDefName,
   472  			},
   473  		},
   474  		Spec: appsv1alpha1.BackupPolicyTemplateSpec{
   475  			ClusterDefRef: clusterDef,
   476  			Identifier:    "fake-identifier",
   477  		},
   478  	}
   479  	return backupPolicyTemplate
   480  }
   481  
   482  func FakeBackupWithCluster(cluster *appsv1alpha1.Cluster, backupName string) *dpv1alpha1.Backup {
   483  	backup := &dpv1alpha1.Backup{
   484  		TypeMeta: metav1.TypeMeta{
   485  			APIVersion: fmt.Sprintf("%s/%s", types.DPAPIGroup, types.DPAPIVersion),
   486  			Kind:       types.KindBackup,
   487  		},
   488  		ObjectMeta: metav1.ObjectMeta{
   489  			Name:      backupName,
   490  			Namespace: Namespace,
   491  			Labels: map[string]string{
   492  				constant.AppInstanceLabelKey: cluster.Name,
   493  				dptypes.ClusterUIDLabelKey:   string(cluster.UID),
   494  			},
   495  		},
   496  	}
   497  	backup.SetCreationTimestamp(metav1.Now())
   498  	return backup
   499  }
   500  
   501  func FakeServices() *corev1.ServiceList {
   502  	cases := []struct {
   503  		exposed    bool
   504  		clusterIP  string
   505  		floatingIP string
   506  	}{
   507  		{false, "", ""},
   508  		{false, "192.168.0.1", ""},
   509  		{true, "192.168.0.1", ""},
   510  		{true, "192.168.0.1", "172.31.0.4"},
   511  	}
   512  
   513  	var services []corev1.Service
   514  	for idx, item := range cases {
   515  		svc := corev1.Service{
   516  			ObjectMeta: metav1.ObjectMeta{
   517  				Name:      fmt.Sprintf("svc-%d", idx),
   518  				Namespace: Namespace,
   519  				Labels: map[string]string{
   520  					constant.AppInstanceLabelKey:    ClusterName,
   521  					constant.KBAppComponentLabelKey: ComponentName,
   522  					constant.AppManagedByLabelKey:   constant.AppName,
   523  				},
   524  			},
   525  			Spec: corev1.ServiceSpec{
   526  				Type:  corev1.ServiceTypeClusterIP,
   527  				Ports: []corev1.ServicePort{{Port: 3306}},
   528  			},
   529  		}
   530  
   531  		if item.clusterIP == "" {
   532  			svc.Spec.ClusterIP = "None"
   533  		} else {
   534  			svc.Spec.ClusterIP = item.clusterIP
   535  		}
   536  
   537  		annotations := make(map[string]string)
   538  		if item.floatingIP != "" {
   539  			annotations[types.ServiceFloatingIPAnnotationKey] = item.floatingIP
   540  		}
   541  		if item.exposed {
   542  			annotations[types.ServiceHAVIPTypeAnnotationKey] = types.ServiceHAVIPTypeAnnotationValue
   543  		}
   544  		svc.ObjectMeta.SetAnnotations(annotations)
   545  
   546  		services = append(services, svc)
   547  	}
   548  	return &corev1.ServiceList{Items: services}
   549  }
   550  
   551  func FakePVCs() *corev1.PersistentVolumeClaimList {
   552  	pvcs := &corev1.PersistentVolumeClaimList{}
   553  	pvc := corev1.PersistentVolumeClaim{
   554  		ObjectMeta: metav1.ObjectMeta{
   555  			Namespace: Namespace,
   556  			Name:      PVCName,
   557  			Labels: map[string]string{
   558  				constant.AppInstanceLabelKey:    ClusterName,
   559  				constant.KBAppComponentLabelKey: ComponentName,
   560  				constant.AppManagedByLabelKey:   constant.AppName,
   561  			},
   562  		},
   563  		Spec: corev1.PersistentVolumeClaimSpec{
   564  			StorageClassName: pointer.String(StorageClassName),
   565  			AccessModes:      []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"},
   566  			Resources: corev1.ResourceRequirements{
   567  				Requests: corev1.ResourceList{
   568  					corev1.ResourceStorage: resource.MustParse("1Gi"),
   569  				},
   570  			},
   571  		},
   572  	}
   573  	pvcs.Items = append(pvcs.Items, pvc)
   574  	return pvcs
   575  }
   576  
   577  func FakeEvents() *corev1.EventList {
   578  	eventList := &corev1.EventList{}
   579  	fakeEvent := func(name string, createTime metav1.Time) corev1.Event {
   580  		e := corev1.Event{}
   581  		e.Name = name
   582  		e.Type = "Warning"
   583  		e.SetCreationTimestamp(createTime)
   584  		e.LastTimestamp = createTime
   585  		return e
   586  	}
   587  
   588  	parseTime := func(t string) time.Time {
   589  		time, _ := time.Parse(time.RFC3339, t)
   590  		return time
   591  	}
   592  
   593  	for _, e := range []struct {
   594  		name       string
   595  		createTime metav1.Time
   596  	}{
   597  		{
   598  			name:       "e1",
   599  			createTime: metav1.NewTime(parseTime("2023-01-04T00:00:00.000Z")),
   600  		},
   601  		{
   602  			name:       "e2",
   603  			createTime: metav1.NewTime(parseTime("2023-01-04T01:00:00.000Z")),
   604  		},
   605  	} {
   606  		eventList.Items = append(eventList.Items, fakeEvent(e.name, e.createTime))
   607  	}
   608  	return eventList
   609  }
   610  
   611  func FakeVolumeSnapshotClass() *snapshotv1.VolumeSnapshotClass {
   612  	return &snapshotv1.VolumeSnapshotClass{
   613  		TypeMeta: metav1.TypeMeta{
   614  			Kind:       "VolumeSnapshotClass",
   615  			APIVersion: "snapshot.storage.k8s.io/v1",
   616  		},
   617  	}
   618  }
   619  
   620  func FakeKBDeploy(version string) *appsv1.Deployment {
   621  	deploy := &appsv1.Deployment{
   622  		TypeMeta: metav1.TypeMeta{
   623  			Kind:       "Deployment",
   624  			APIVersion: "apps/v1",
   625  		},
   626  	}
   627  	deploy.SetLabels(map[string]string{
   628  		"app.kubernetes.io/name":      types.KubeBlocksChartName,
   629  		"app.kubernetes.io/component": "apps",
   630  	})
   631  	if len(version) > 0 {
   632  		deploy.Labels["app.kubernetes.io/version"] = version
   633  	}
   634  	return deploy
   635  }
   636  
   637  func FakeAddon(name string) *extensionsv1alpha1.Addon {
   638  	addon := &extensionsv1alpha1.Addon{
   639  		TypeMeta: metav1.TypeMeta{
   640  			APIVersion: fmt.Sprintf("%s/%s", types.ExtensionsAPIGroup, types.ExtensionsAPIVersion),
   641  			Kind:       "Addon",
   642  		},
   643  		ObjectMeta: metav1.ObjectMeta{
   644  			Name:      name,
   645  			Namespace: Namespace,
   646  		},
   647  		Spec: extensionsv1alpha1.AddonSpec{
   648  			Installable: &extensionsv1alpha1.InstallableSpec{
   649  				Selectors: []extensionsv1alpha1.SelectorRequirement{
   650  					{Key: extensionsv1alpha1.KubeGitVersion, Operator: extensionsv1alpha1.Contains, Values: []string{"k3s"}},
   651  				},
   652  			},
   653  		},
   654  	}
   655  	addon.SetCreationTimestamp(metav1.Now())
   656  	return addon
   657  }
   658  
   659  func FakeConfigMap(cmName string, namespace string, data map[string]string) *corev1.ConfigMap {
   660  	cm := &corev1.ConfigMap{
   661  		TypeMeta: metav1.TypeMeta{
   662  			APIVersion: "v1",
   663  			Kind:       "ConfigMap",
   664  		},
   665  		ObjectMeta: metav1.ObjectMeta{
   666  			Name:      cmName,
   667  			Namespace: Namespace,
   668  		},
   669  		Data: data,
   670  	}
   671  	if namespace != "" {
   672  		cm.Namespace = namespace
   673  	}
   674  	return cm
   675  }
   676  
   677  func FakeConfigConstraint(ccName string) *appsv1alpha1.ConfigConstraint {
   678  	cm := &appsv1alpha1.ConfigConstraint{
   679  		ObjectMeta: metav1.ObjectMeta{
   680  			Name: ccName,
   681  		},
   682  		Spec: appsv1alpha1.ConfigConstraintSpec{
   683  			FormatterConfig: &appsv1alpha1.FormatterConfig{},
   684  		},
   685  	}
   686  	return cm
   687  }
   688  
   689  func FakeStorageClass(name string, isDefault bool) *storagev1.StorageClass {
   690  	storageClassObj := &storagev1.StorageClass{
   691  		TypeMeta: metav1.TypeMeta{
   692  			Kind:       "StorageClass",
   693  			APIVersion: "storage.k8s.io/v1",
   694  		},
   695  		ObjectMeta: metav1.ObjectMeta{
   696  			Name: name,
   697  		},
   698  	}
   699  	if isDefault {
   700  		storageClassObj.ObjectMeta.Annotations = make(map[string]string)
   701  		storageClassObj.ObjectMeta.Annotations[storage.IsDefaultStorageClassAnnotation] = "true"
   702  	}
   703  	return storageClassObj
   704  }
   705  
   706  func FakeServiceAccount(name string) *corev1.ServiceAccount {
   707  	return &corev1.ServiceAccount{
   708  		ObjectMeta: metav1.ObjectMeta{
   709  			Name:      name,
   710  			Namespace: Namespace,
   711  			Labels: map[string]string{
   712  				constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   713  				constant.AppNameLabelKey:     KubeBlocksChartName},
   714  		},
   715  	}
   716  }
   717  
   718  func FakeClusterRole(name string) *rbacv1.ClusterRole {
   719  	return &rbacv1.ClusterRole{
   720  		ObjectMeta: metav1.ObjectMeta{
   721  			Name: name,
   722  			Labels: map[string]string{
   723  				constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   724  				constant.AppNameLabelKey:     KubeBlocksChartName},
   725  		},
   726  		Rules: []rbacv1.PolicyRule{
   727  			{
   728  				APIGroups: []string{"*"},
   729  				Resources: []string{"*"},
   730  				Verbs:     []string{"*"},
   731  			},
   732  		},
   733  	}
   734  }
   735  
   736  func FakeClusterRoleBinding(name string, sa *corev1.ServiceAccount, clusterRole *rbacv1.ClusterRole) *rbacv1.ClusterRoleBinding {
   737  	return &rbacv1.ClusterRoleBinding{
   738  		ObjectMeta: metav1.ObjectMeta{
   739  			Name: name,
   740  			Labels: map[string]string{
   741  				constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   742  				constant.AppNameLabelKey:     KubeBlocksChartName},
   743  		},
   744  		RoleRef: rbacv1.RoleRef{
   745  			Kind: clusterRole.Kind,
   746  			Name: clusterRole.Name,
   747  		},
   748  		Subjects: []rbacv1.Subject{
   749  			{
   750  				Kind:      "ServiceAccount",
   751  				Name:      sa.Name,
   752  				Namespace: sa.Namespace,
   753  			},
   754  		},
   755  	}
   756  }
   757  
   758  func FakeRole(name string) *rbacv1.Role {
   759  	return &rbacv1.Role{
   760  		ObjectMeta: metav1.ObjectMeta{
   761  			Name: name,
   762  			Labels: map[string]string{
   763  				constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   764  				constant.AppNameLabelKey:     KubeBlocksChartName},
   765  		},
   766  		Rules: []rbacv1.PolicyRule{
   767  			{
   768  				APIGroups: []string{"*"},
   769  				Resources: []string{"*"},
   770  				Verbs:     []string{"*"},
   771  			},
   772  		},
   773  	}
   774  }
   775  
   776  func FakeRoleBinding(name string, sa *corev1.ServiceAccount, role *rbacv1.Role) *rbacv1.RoleBinding {
   777  	return &rbacv1.RoleBinding{
   778  		ObjectMeta: metav1.ObjectMeta{
   779  			Name:      name,
   780  			Namespace: Namespace,
   781  			Labels: map[string]string{
   782  				constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   783  				constant.AppNameLabelKey:     KubeBlocksChartName},
   784  		},
   785  		RoleRef: rbacv1.RoleRef{
   786  			Kind: role.Kind,
   787  			Name: role.Name,
   788  		},
   789  		Subjects: []rbacv1.Subject{
   790  			{
   791  				Kind:      "ServiceAccount",
   792  				Name:      sa.Name,
   793  				Namespace: sa.Namespace,
   794  			},
   795  		},
   796  	}
   797  }
   798  
   799  func FakeDeploy(name string, namespace string, extraLabels map[string]string) *appsv1.Deployment {
   800  	labels := map[string]string{
   801  		constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   802  	}
   803  	// extraLabels will override the labels above if there is a conflict
   804  	for k, v := range extraLabels {
   805  		labels[k] = v
   806  	}
   807  	labels["app"] = name
   808  
   809  	return &appsv1.Deployment{
   810  		ObjectMeta: metav1.ObjectMeta{
   811  			Name:      name,
   812  			Namespace: namespace,
   813  			Labels:    labels,
   814  		},
   815  		Spec: appsv1.DeploymentSpec{
   816  			Replicas: pointer.Int32(1),
   817  			Selector: &metav1.LabelSelector{
   818  				MatchLabels: labels,
   819  			},
   820  			Template: corev1.PodTemplateSpec{
   821  				ObjectMeta: metav1.ObjectMeta{
   822  					Labels: labels,
   823  				},
   824  			},
   825  		},
   826  	}
   827  }
   828  
   829  func FakeStatefulSet(name string, namespace string, extraLabels map[string]string) *appsv1.StatefulSet {
   830  	labels := map[string]string{
   831  		constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   832  	}
   833  	// extraLabels will override the labels above if there is a conflict
   834  	for k, v := range extraLabels {
   835  		labels[k] = v
   836  	}
   837  	labels["app"] = name
   838  	return &appsv1.StatefulSet{
   839  		ObjectMeta: metav1.ObjectMeta{
   840  			Name:      name,
   841  			Namespace: namespace,
   842  			Labels:    labels,
   843  		},
   844  		Spec: appsv1.StatefulSetSpec{
   845  			Replicas: pointer.Int32(1),
   846  			Selector: &metav1.LabelSelector{
   847  				MatchLabels: labels,
   848  			},
   849  			Template: corev1.PodTemplateSpec{
   850  				ObjectMeta: metav1.ObjectMeta{
   851  					Labels: labels,
   852  				},
   853  			},
   854  		},
   855  		Status: appsv1.StatefulSetStatus{
   856  			Replicas: 1,
   857  		},
   858  	}
   859  }
   860  
   861  func FakePodForSts(sts *appsv1.StatefulSet) *corev1.PodList {
   862  	pods := &corev1.PodList{}
   863  	for i := 0; i < int(*sts.Spec.Replicas); i++ {
   864  		pod := &corev1.Pod{
   865  			ObjectMeta: metav1.ObjectMeta{
   866  				Name:      fmt.Sprintf("%s-%d", sts.Name, i),
   867  				Namespace: sts.Namespace,
   868  				Labels:    sts.Spec.Template.Labels,
   869  			},
   870  			Spec: corev1.PodSpec{
   871  				Containers: []corev1.Container{
   872  					{
   873  						Name:  sts.Name,
   874  						Image: "fake-image",
   875  					},
   876  				},
   877  			},
   878  			Status: corev1.PodStatus{
   879  				Phase: corev1.PodRunning,
   880  			},
   881  		}
   882  		pods.Items = append(pods.Items, *pod)
   883  	}
   884  	return pods
   885  }
   886  
   887  func FakeJob(name string, namespace string, extraLabels map[string]string) *batchv1.Job {
   888  	labels := map[string]string{
   889  		constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   890  	}
   891  	// extraLabels will override the labels above if there is a conflict
   892  	for k, v := range extraLabels {
   893  		labels[k] = v
   894  	}
   895  	labels["app"] = name
   896  
   897  	return &batchv1.Job{
   898  		ObjectMeta: metav1.ObjectMeta{
   899  			Name:      name,
   900  			Namespace: namespace,
   901  			Labels:    labels,
   902  		},
   903  		Spec: batchv1.JobSpec{
   904  			Completions: pointer.Int32(1),
   905  			Template: corev1.PodTemplateSpec{
   906  				ObjectMeta: metav1.ObjectMeta{
   907  					Labels: labels,
   908  				},
   909  			},
   910  		},
   911  		Status: batchv1.JobStatus{
   912  			Active: 1,
   913  			Ready:  pointer.Int32(1),
   914  		},
   915  	}
   916  }
   917  
   918  func FakeCronJob(name string, namespace string, extraLabels map[string]string) *batchv1.CronJob {
   919  	labels := map[string]string{
   920  		constant.AppInstanceLabelKey: types.KubeBlocksReleaseName,
   921  	}
   922  	// extraLabels will override the labels above if there is a conflict
   923  	for k, v := range extraLabels {
   924  		labels[k] = v
   925  	}
   926  	labels["app"] = name
   927  
   928  	return &batchv1.CronJob{
   929  		ObjectMeta: metav1.ObjectMeta{
   930  			Name:      name,
   931  			Namespace: namespace,
   932  			Labels:    labels,
   933  		},
   934  		Spec: batchv1.CronJobSpec{
   935  			Schedule: "*/1 * * * *",
   936  			JobTemplate: batchv1.JobTemplateSpec{
   937  				ObjectMeta: metav1.ObjectMeta{
   938  					Labels: labels,
   939  				},
   940  			},
   941  		},
   942  	}
   943  }
   944  
   945  func FakeResourceNotFound(versionResource schema.GroupVersionResource, name string) *metav1.Status {
   946  	return &metav1.Status{
   947  		TypeMeta: metav1.TypeMeta{
   948  			Kind:       "Status",
   949  			APIVersion: "v1",
   950  		},
   951  		Status:  "Failure",
   952  		Message: fmt.Sprintf("%s.%s \"%s\" not found", versionResource.Resource, versionResource.Group, name),
   953  		Reason:  "NotFound",
   954  		Details: nil,
   955  		Code:    404,
   956  	}
   957  }
   958  
   959  func FakePodChaos(name, namespace string) *chaosmeshv1alpha1.PodChaos {
   960  	return &chaosmeshv1alpha1.PodChaos{
   961  		TypeMeta: metav1.TypeMeta{
   962  			Kind:       "PodChaos",
   963  			APIVersion: "chaos-mesh.org/v1alpha1",
   964  		},
   965  		ObjectMeta: metav1.ObjectMeta{
   966  			Name:      name,
   967  			Namespace: namespace,
   968  		},
   969  		Spec: chaosmeshv1alpha1.PodChaosSpec{
   970  			ContainerSelector: chaosmeshv1alpha1.ContainerSelector{
   971  				PodSelector: chaosmeshv1alpha1.PodSelector{
   972  					Selector: chaosmeshv1alpha1.PodSelectorSpec{
   973  						GenericSelectorSpec: chaosmeshv1alpha1.GenericSelectorSpec{
   974  							Namespaces: []string{namespace},
   975  						},
   976  					},
   977  				},
   978  			},
   979  			Action: chaosmeshv1alpha1.PodKillAction,
   980  		},
   981  	}
   982  }
   983  
   984  func FakeEventForObject(name string, namespace string, object string) *corev1.Event {
   985  	return &corev1.Event{
   986  		TypeMeta: metav1.TypeMeta{
   987  			Kind:       "Event",
   988  			APIVersion: "v1",
   989  		},
   990  		ObjectMeta: metav1.ObjectMeta{
   991  			Name:      name,
   992  			Namespace: namespace,
   993  		},
   994  		InvolvedObject: corev1.ObjectReference{
   995  			Name: object,
   996  		},
   997  	}
   998  }
   999  
  1000  func FakeStorageProvider(name string, mutateFunc func(obj *storagev1alpha1.StorageProvider)) *storagev1alpha1.StorageProvider {
  1001  	storageProvider := &storagev1alpha1.StorageProvider{
  1002  		TypeMeta: metav1.TypeMeta{
  1003  			APIVersion: fmt.Sprintf("%s/%s", types.StorageAPIGroup, types.StorageAPIVersion),
  1004  			Kind:       "StorageProvider",
  1005  		},
  1006  		ObjectMeta: metav1.ObjectMeta{
  1007  			Name: name,
  1008  		},
  1009  		Spec: storagev1alpha1.StorageProviderSpec{
  1010  			CSIDriverName: "fake-csi-s3",
  1011  			CSIDriverSecretTemplate: `
  1012  accessKeyId: {{ index .Parameters "accessKeyId" }}
  1013  secretAccessKey: {{ index .Parameters "secretAccessKey" }}
  1014  `,
  1015  			StorageClassTemplate: `
  1016  bucket: {{ index .Parameters "bucket" }}
  1017  region: {{ index .Parameters "region" }}
  1018  endpoint: {{ index .Parameters "endpoint" }}
  1019  mountOptions: {{ index .Parameters "mountOptions" | default "" }}
  1020  `,
  1021  			ParametersSchema: &storagev1alpha1.ParametersSchema{
  1022  				OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
  1023  					Type: "object",
  1024  					Properties: map[string]apiextensionsv1.JSONSchemaProps{
  1025  						"accessKeyId":     {Type: "string"},
  1026  						"secretAccessKey": {Type: "string"},
  1027  						"bucket":          {Type: "string"},
  1028  						"region": {
  1029  							Type: "string",
  1030  							Enum: []apiextensionsv1.JSON{{Raw: []byte(`""`)}, {Raw: []byte(`"us-east-1"`)}, {Raw: []byte(`"us-west-1"`)}},
  1031  						},
  1032  						"endpoint":     {Type: "string"},
  1033  						"mountOptions": {Type: "string"},
  1034  					},
  1035  					Required: []string{
  1036  						"accessKeyId",
  1037  						"secretAccessKey",
  1038  					},
  1039  				},
  1040  				CredentialFields: []string{
  1041  					"accessKeyId",
  1042  					"secretAccessKey",
  1043  				},
  1044  			},
  1045  		},
  1046  	}
  1047  	storageProvider.SetCreationTimestamp(metav1.Now())
  1048  	if mutateFunc != nil {
  1049  		mutateFunc(storageProvider)
  1050  	}
  1051  	return storageProvider
  1052  }
  1053  
  1054  func FakeBackupRepo(name string, isDefault bool) *dpv1alpha1.BackupRepo {
  1055  	backupRepo := &dpv1alpha1.BackupRepo{
  1056  		TypeMeta: metav1.TypeMeta{
  1057  			APIVersion: fmt.Sprintf("%s/%s", types.DPAPIGroup, types.DPAPIVersion),
  1058  			Kind:       "BackupRepo",
  1059  		},
  1060  		ObjectMeta: metav1.ObjectMeta{
  1061  			Name: name,
  1062  		},
  1063  		Spec: dpv1alpha1.BackupRepoSpec{
  1064  			StorageProviderRef: "fake-storage-provider",
  1065  			PVReclaimPolicy:    "Retain",
  1066  		},
  1067  	}
  1068  	if isDefault {
  1069  		backupRepo.Annotations = map[string]string{
  1070  			dptypes.DefaultBackupRepoAnnotationKey: "true",
  1071  		}
  1072  	}
  1073  	return backupRepo
  1074  }
  1075  
  1076  func FakeClusterList() *appsv1alpha1.ClusterList {
  1077  	clusters := &appsv1alpha1.ClusterList{
  1078  		ListMeta: metav1.ListMeta{
  1079  			ResourceVersion: "15",
  1080  		},
  1081  		Items: []appsv1alpha1.Cluster{
  1082  			*FakeCluster(ClusterName, Namespace),
  1083  			*FakeCluster(ClusterName+"-other", Namespace),
  1084  		},
  1085  	}
  1086  	clusters.Items = append(clusters.Items, *FakeCluster(ClusterName, Namespace))
  1087  	return clusters
  1088  }
  1089  
  1090  func FakeServiceRef(serviceRefName string) appsv1alpha1.ServiceRefDeclaration {
  1091  	return appsv1alpha1.ServiceRefDeclaration{
  1092  		Name: serviceRefName,
  1093  		ServiceRefDeclarationSpecs: []appsv1alpha1.ServiceRefDeclarationSpec{
  1094  			{
  1095  				ServiceKind:    "mysql",
  1096  				ServiceVersion: "8.0.\\d{1,2}$",
  1097  			},
  1098  		},
  1099  	}
  1100  }