-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(core): rename core resources (#115)
Rename cdi, kubevirt core resources. Replace "cdi" to "cdi-internal-virtualziation", "kubevirt" to "kubevirt-internal-virtualziation". --------- Signed-off-by: yaroslavborbat <[email protected]>
- Loading branch information
1 parent
38c8d6c
commit a8451b6
Showing
7 changed files
with
383 additions
and
13 deletions.
There are no files selected for viewing
192 changes: 192 additions & 0 deletions
192
images/cdi-artifact/patches/008-rename-core-resources.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
diff --git a/pkg/operator/resources/cluster/apiserver.go b/pkg/operator/resources/cluster/apiserver.go | ||
index 5e8432713..adf8093fa 100644 | ||
--- a/pkg/operator/resources/cluster/apiserver.go | ||
+++ b/pkg/operator/resources/cluster/apiserver.go | ||
@@ -215,7 +215,7 @@ func createDataImportCronValidatingWebhook(namespace string, c client.Client, l | ||
Kind: "ValidatingWebhookConfiguration", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "cdi-api-dataimportcron-validate", | ||
+ Name: "cdi-internal-virtualization-api-dataimportcron-validate", | ||
Labels: map[string]string{ | ||
utils.CDILabel: apiServerServiceName, | ||
}, | ||
@@ -282,7 +282,7 @@ func createPopulatorsValidatingWebhook(namespace string, c client.Client, l logr | ||
Kind: "ValidatingWebhookConfiguration", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "cdi-api-populator-validate", | ||
+ Name: "cdi-internal-virtualization-api-populator-validate", | ||
Labels: map[string]string{ | ||
utils.CDILabel: apiServerServiceName, | ||
}, | ||
@@ -349,7 +349,7 @@ func createDataVolumeValidatingWebhook(namespace string, c client.Client, l logr | ||
Kind: "ValidatingWebhookConfiguration", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "cdi-api-datavolume-validate", | ||
+ Name: "cdi-internal-virtualization-api-datavolume-validate", | ||
Labels: map[string]string{ | ||
utils.CDILabel: apiServerServiceName, | ||
}, | ||
@@ -416,7 +416,7 @@ func createCDIValidatingWebhook(namespace string, c client.Client, l logr.Logger | ||
Kind: "ValidatingWebhookConfiguration", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "cdi-api-validate", | ||
+ Name: "cdi-internal-virtualization-api-validate", | ||
Labels: map[string]string{ | ||
utils.CDILabel: apiServerServiceName, | ||
}, | ||
@@ -485,7 +485,7 @@ func createObjectTransferValidatingWebhook(namespace string, c client.Client, l | ||
Kind: "ValidatingWebhookConfiguration", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "objecttransfer-api-validate", | ||
+ Name: "cdi-internal-virtualization-objecttransfer-api-validate", | ||
Labels: map[string]string{ | ||
utils.CDILabel: apiServerServiceName, | ||
}, | ||
@@ -558,7 +558,7 @@ func createDataVolumeMutatingWebhook(namespace string, c client.Client, l logr.L | ||
Kind: "MutatingWebhookConfiguration", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "cdi-api-datavolume-mutate", | ||
+ Name: "cdi-internal-virtualization-api-datavolume-mutate", | ||
Labels: map[string]string{ | ||
utils.CDILabel: apiServerServiceName, | ||
}, | ||
@@ -626,10 +626,12 @@ func getAPIServerCABundle(namespace string, c client.Client, l logr.Logger) []by | ||
return nil | ||
} | ||
|
||
+const apiServerWrapName = "cdi-internal-virtualization-apiserver" | ||
+ | ||
func createAPIServerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { | ||
- return utils.ResourceBuilder.CreateClusterRoleBinding(apiServerResourceName, apiServerResourceName, apiServerResourceName, namespace) | ||
+ return utils.ResourceBuilder.CreateClusterRoleBinding(apiServerWrapName, apiServerWrapName, apiServerResourceName, namespace) | ||
} | ||
|
||
func createAPIServerClusterRole() *rbacv1.ClusterRole { | ||
- return utils.ResourceBuilder.CreateClusterRole(apiServerResourceName, getAPIServerClusterPolicyRules()) | ||
+ return utils.ResourceBuilder.CreateClusterRole(apiServerWrapName, getAPIServerClusterPolicyRules()) | ||
} | ||
diff --git a/pkg/operator/resources/cluster/controller.go b/pkg/operator/resources/cluster/controller.go | ||
index d29b0dd16..875afaf61 100644 | ||
--- a/pkg/operator/resources/cluster/controller.go | ||
+++ b/pkg/operator/resources/cluster/controller.go | ||
@@ -26,6 +26,9 @@ import ( | ||
const ( | ||
controllerServiceAccountName = "cdi-sa" | ||
controlerClusterRoleName = "cdi" | ||
+ | ||
+ wrapServiceAccountName = "cdi-internal-virtualization-sa" | ||
+ wrapClusterRoleName = "cdi-internal-virtualization" | ||
) | ||
|
||
func createControllerResources(args *FactoryArgs) []client.Object { | ||
@@ -36,7 +39,7 @@ func createControllerResources(args *FactoryArgs) []client.Object { | ||
} | ||
|
||
func createControllerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { | ||
- return utils.ResourceBuilder.CreateClusterRoleBinding(controllerServiceAccountName, controlerClusterRoleName, controllerServiceAccountName, namespace) | ||
+ return utils.ResourceBuilder.CreateClusterRoleBinding(wrapServiceAccountName, wrapClusterRoleName, controllerServiceAccountName, namespace) | ||
} | ||
|
||
func getControllerClusterPolicyRules() []rbacv1.PolicyRule { | ||
@@ -257,5 +260,5 @@ func getControllerClusterPolicyRules() []rbacv1.PolicyRule { | ||
} | ||
|
||
func createControllerClusterRole() *rbacv1.ClusterRole { | ||
- return utils.ResourceBuilder.CreateClusterRole(controlerClusterRoleName, getControllerClusterPolicyRules()) | ||
+ return utils.ResourceBuilder.CreateClusterRole(wrapClusterRoleName, getControllerClusterPolicyRules()) | ||
} | ||
diff --git a/pkg/operator/resources/cluster/cronjob.go b/pkg/operator/resources/cluster/cronjob.go | ||
index 71b2fa0f7..bf45a6480 100644 | ||
--- a/pkg/operator/resources/cluster/cronjob.go | ||
+++ b/pkg/operator/resources/cluster/cronjob.go | ||
@@ -53,10 +53,12 @@ func getCronJobClusterPolicyRules() []rbacv1.PolicyRule { | ||
} | ||
} | ||
|
||
+const cronJobWrapName = "cdi-internal-virtualization-cronjob" | ||
+ | ||
func createCronJobClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { | ||
- return utils.ResourceBuilder.CreateClusterRoleBinding(cronJobResourceName, cronJobResourceName, cronJobResourceName, namespace) | ||
+ return utils.ResourceBuilder.CreateClusterRoleBinding(cronJobWrapName, cronJobWrapName, cronJobResourceName, namespace) | ||
} | ||
|
||
func createCronJobClusterRole() *rbacv1.ClusterRole { | ||
- return utils.ResourceBuilder.CreateClusterRole(cronJobResourceName, getCronJobClusterPolicyRules()) | ||
+ return utils.ResourceBuilder.CreateClusterRole(cronJobWrapName, getCronJobClusterPolicyRules()) | ||
} | ||
diff --git a/pkg/operator/resources/cluster/rbac.go b/pkg/operator/resources/cluster/rbac.go | ||
index 264b83891..a2a968b41 100644 | ||
--- a/pkg/operator/resources/cluster/rbac.go | ||
+++ b/pkg/operator/resources/cluster/rbac.go | ||
@@ -26,11 +26,11 @@ import ( | ||
|
||
func createAggregateClusterRoles(_ *FactoryArgs) []client.Object { | ||
return []client.Object{ | ||
- utils.ResourceBuilder.CreateAggregateClusterRole("cdi.kubevirt.io:admin", "admin", getAdminPolicyRules()), | ||
- utils.ResourceBuilder.CreateAggregateClusterRole("cdi.kubevirt.io:edit", "edit", getEditPolicyRules()), | ||
- utils.ResourceBuilder.CreateAggregateClusterRole("cdi.kubevirt.io:view", "view", getViewPolicyRules()), | ||
- createConfigReaderClusterRole("cdi.kubevirt.io:config-reader"), | ||
- createConfigReaderClusterRoleBinding("cdi.kubevirt.io:config-reader"), | ||
+ utils.ResourceBuilder.CreateAggregateClusterRole("cdi.internal.virtualization.deckhouse.io:admin", "admin", getAdminPolicyRules()), | ||
+ utils.ResourceBuilder.CreateAggregateClusterRole("cdi.internal.virtualization.deckhouse.io:edit", "edit", getEditPolicyRules()), | ||
+ utils.ResourceBuilder.CreateAggregateClusterRole("cdi.internal.virtualization.deckhouse.io:view", "view", getViewPolicyRules()), | ||
+ createConfigReaderClusterRole("cdi.internal.virtualization.deckhouse.io:config-reader"), | ||
+ createConfigReaderClusterRoleBinding("cdi.internal.virtualization.deckhouse.io:config-reader"), | ||
} | ||
} | ||
|
||
diff --git a/pkg/operator/resources/cluster/uploadproxy.go b/pkg/operator/resources/cluster/uploadproxy.go | ||
index a9ac62765..e22a871c7 100644 | ||
--- a/pkg/operator/resources/cluster/uploadproxy.go | ||
+++ b/pkg/operator/resources/cluster/uploadproxy.go | ||
@@ -51,10 +51,12 @@ func getUploadProxyClusterPolicyRules() []rbacv1.PolicyRule { | ||
} | ||
} | ||
|
||
+const uploadProxyWrapName = "cdi-internal-virtualization-uploadproxy" | ||
+ | ||
func createUploadProxyClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { | ||
- return utils.ResourceBuilder.CreateClusterRoleBinding(uploadProxyResourceName, uploadProxyResourceName, uploadProxyResourceName, namespace) | ||
+ return utils.ResourceBuilder.CreateClusterRoleBinding(uploadProxyWrapName, uploadProxyWrapName, uploadProxyResourceName, namespace) | ||
} | ||
|
||
func createUploadProxyClusterRole() *rbacv1.ClusterRole { | ||
- return utils.ResourceBuilder.CreateClusterRole(uploadProxyResourceName, getUploadProxyClusterPolicyRules()) | ||
+ return utils.ResourceBuilder.CreateClusterRole(uploadProxyWrapName, getUploadProxyClusterPolicyRules()) | ||
} | ||
diff --git a/pkg/operator/resources/operator/operator.go b/pkg/operator/resources/operator/operator.go | ||
index 1ad35841f..01ae5e72e 100644 | ||
--- a/pkg/operator/resources/operator/operator.go | ||
+++ b/pkg/operator/resources/operator/operator.go | ||
@@ -129,11 +129,11 @@ func getClusterPolicyRules() []rbacv1.PolicyRule { | ||
"validatingwebhookconfigurations", | ||
}, | ||
ResourceNames: []string{ | ||
- "cdi-api-dataimportcron-validate", | ||
- "cdi-api-populator-validate", | ||
- "cdi-api-datavolume-validate", | ||
- "cdi-api-validate", | ||
- "objecttransfer-api-validate", | ||
+ "cdi-internal-virtualization-api-dataimportcron-validate", | ||
+ "cdi-internal-virtualization-api-populator-validate", | ||
+ "cdi-internal-virtualization-api-datavolume-validate", | ||
+ "cdi-internal-virtualization-api-validate", | ||
+ "cdi-internal-virtualization-objecttransfer-api-validate", | ||
}, | ||
Verbs: []string{ | ||
"get", | ||
@@ -149,7 +149,7 @@ func getClusterPolicyRules() []rbacv1.PolicyRule { | ||
"mutatingwebhookconfigurations", | ||
}, | ||
ResourceNames: []string{ | ||
- "cdi-api-datavolume-mutate", | ||
+ "cdi-internal-virtualization-api-datavolume-mutate", | ||
}, | ||
Verbs: []string{ | ||
"get", |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 172 additions & 0 deletions
172
images/virt-artifact/patches/015-rename-core-resources.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
diff --git a/manifests/generated/kubevirt-priority-class.yaml b/manifests/generated/kubevirt-priority-class.yaml | ||
index e8dfe36c2..0f57dd6a8 100644 | ||
--- a/manifests/generated/kubevirt-priority-class.yaml | ||
+++ b/manifests/generated/kubevirt-priority-class.yaml | ||
@@ -3,5 +3,5 @@ apiVersion: scheduling.k8s.io/v1 | ||
description: This priority class should be used for KubeVirt core components only. | ||
kind: PriorityClass | ||
metadata: | ||
- name: kubevirt-cluster-critical | ||
+ name: kubevirt-internal-virtualization-cluster-critical | ||
value: 1000000000 | ||
diff --git a/manifests/generated/operator-csv.yaml.in b/manifests/generated/operator-csv.yaml.in | ||
index b0a4b48e9..245e32dfb 100644 | ||
--- a/manifests/generated/operator-csv.yaml.in | ||
+++ b/manifests/generated/operator-csv.yaml.in | ||
@@ -1356,7 +1356,7 @@ spec: | ||
name: profile-data | ||
nodeSelector: | ||
kubernetes.io/os: linux | ||
- priorityClassName: kubevirt-cluster-critical | ||
+ priorityClassName: kubevirt-internal-virtualization-cluster-critical | ||
securityContext: | ||
runAsNonRoot: true | ||
seccompProfile: | ||
diff --git a/manifests/release/kubevirt-operator.yaml.in b/manifests/release/kubevirt-operator.yaml.in | ||
index 6ac36d99b..d7bfbd010 100644 | ||
--- a/manifests/release/kubevirt-operator.yaml.in | ||
+++ b/manifests/release/kubevirt-operator.yaml.in | ||
@@ -11,7 +11,7 @@ metadata: | ||
apiVersion: scheduling.k8s.io/v1 | ||
kind: PriorityClass | ||
metadata: | ||
- name: kubevirt-cluster-critical | ||
+ name: kubevirt-internal-virtualization-cluster-critical | ||
value: 1000000000 | ||
globalDefault: false | ||
description: "This priority class should be used for core kubevirt components only." | ||
diff --git a/pkg/virt-controller/watch/drain/disruptionbudget/disruptionbudget.go b/pkg/virt-controller/watch/drain/disruptionbudget/disruptionbudget.go | ||
index 228518871..55ce72b6c 100644 | ||
--- a/pkg/virt-controller/watch/drain/disruptionbudget/disruptionbudget.go | ||
+++ b/pkg/virt-controller/watch/drain/disruptionbudget/disruptionbudget.go | ||
@@ -485,7 +485,10 @@ func (c *DisruptionBudgetController) createPDB(key string, vmi *virtv1.VirtualMa | ||
OwnerReferences: []v1.OwnerReference{ | ||
*v1.NewControllerRef(vmi, virtv1.VirtualMachineInstanceGroupVersionKind), | ||
}, | ||
- GenerateName: "kubevirt-disruption-budget-", | ||
+ GenerateName: "kubevirt-internal-virtualization-disruption-budget-", | ||
+ Labels: map[string]string{ | ||
+ virtv1.VirtualMachineNameLabel: vmi.GetName(), | ||
+ }, | ||
}, | ||
Spec: policyv1.PodDisruptionBudgetSpec{ | ||
MinAvailable: &minAvailable, | ||
diff --git a/pkg/virt-operator/resource/generate/components/crds.go b/pkg/virt-operator/resource/generate/components/crds.go | ||
index 822f3d82b..36126ef43 100644 | ||
--- a/pkg/virt-operator/resource/generate/components/crds.go | ||
+++ b/pkg/virt-operator/resource/generate/components/crds.go | ||
@@ -862,7 +862,7 @@ func NewKubeVirtPriorityClassCR() *schedulingv1.PriorityClass { | ||
Kind: "PriorityClass", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "kubevirt-cluster-critical", | ||
+ Name: "kubevirt-internal-virtualization-cluster-critical", | ||
}, | ||
// 1 billion is the highest value we can set | ||
// https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass | ||
diff --git a/pkg/virt-operator/resource/generate/components/deployments.go b/pkg/virt-operator/resource/generate/components/deployments.go | ||
index 4d00a423a..ced56e776 100644 | ||
--- a/pkg/virt-operator/resource/generate/components/deployments.go | ||
+++ b/pkg/virt-operator/resource/generate/components/deployments.go | ||
@@ -166,7 +166,7 @@ func newPodTemplateSpec(podName, imageName, repository, version, productName, pr | ||
Name: podName, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
- PriorityClassName: "kubevirt-cluster-critical", | ||
+ PriorityClassName: "kubevirt-internal-virtualization-cluster-critical", | ||
Affinity: podAffinity, | ||
Tolerations: criticalAddonsToleration(), | ||
Containers: []corev1.Container{ | ||
@@ -529,7 +529,7 @@ func NewOperatorDeployment(namespace, repository, imagePrefix, version, verbosit | ||
Name: VirtOperatorName, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
- PriorityClassName: "kubevirt-cluster-critical", | ||
+ PriorityClassName: "kubevirt-internal-virtualization-cluster-critical", | ||
Tolerations: criticalAddonsToleration(), | ||
Affinity: podAntiAffinity, | ||
ServiceAccountName: "kubevirt-operator", | ||
diff --git a/pkg/virt-operator/resource/generate/components/serviceaccountnames.go b/pkg/virt-operator/resource/generate/components/serviceaccountnames.go | ||
index 0948629bb..9aca3b3bd 100644 | ||
--- a/pkg/virt-operator/resource/generate/components/serviceaccountnames.go | ||
+++ b/pkg/virt-operator/resource/generate/components/serviceaccountnames.go | ||
@@ -1,9 +1,9 @@ | ||
package components | ||
|
||
const ( | ||
- ApiServiceAccountName = "kubevirt-apiserver" | ||
- ControllerServiceAccountName = "kubevirt-controller" | ||
- ExportProxyServiceAccountName = "kubevirt-exportproxy" | ||
- HandlerServiceAccountName = "kubevirt-handler" | ||
+ ApiServiceAccountName = "kubevirt-internal-virtualization-apiserver" | ||
+ ControllerServiceAccountName = "kubevirt-internal-virtualization-controller" | ||
+ ExportProxyServiceAccountName = "kubevirt-internal-virtualization-exportproxy" | ||
+ HandlerServiceAccountName = "kubevirt-internal-virtualization-handler" | ||
OperatorServiceAccountName = "kubevirt-operator" | ||
) | ||
diff --git a/pkg/virt-operator/resource/generate/rbac/apiserver.go b/pkg/virt-operator/resource/generate/rbac/apiserver.go | ||
index 932f7391e..76c79d452 100644 | ||
--- a/pkg/virt-operator/resource/generate/rbac/apiserver.go | ||
+++ b/pkg/virt-operator/resource/generate/rbac/apiserver.go | ||
@@ -294,7 +294,7 @@ func newApiServerAuthDelegatorClusterRoleBinding(namespace string) *rbacv1.Clust | ||
Kind: "ClusterRoleBinding", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "kubevirt-apiserver-auth-delegator", | ||
+ Name: "kubevirt-internal-virtualization-apiserver-auth-delegator", | ||
Labels: map[string]string{ | ||
virtv1.AppLabel: "", | ||
}, | ||
diff --git a/pkg/virt-operator/resource/generate/rbac/cluster.go b/pkg/virt-operator/resource/generate/rbac/cluster.go | ||
index 6ba13c849..12b7ccaa2 100644 | ||
--- a/pkg/virt-operator/resource/generate/rbac/cluster.go | ||
+++ b/pkg/virt-operator/resource/generate/rbac/cluster.go | ||
@@ -37,7 +37,7 @@ const ( | ||
GroupNameClone = "clone.kubevirt.io" | ||
GroupNameInstancetype = "instancetype.kubevirt.io" | ||
GroupNamePool = "pool.kubevirt.io" | ||
- NameDefault = "kubevirt.io:default" | ||
+ NameDefault = "kubevirt.internal.virtualization.deckhouse.io:default" | ||
VMInstancesGuestOSInfo = "virtualmachineinstances/guestosinfo" | ||
VMInstancesFileSysList = "virtualmachineinstances/filesystemlist" | ||
VMInstancesUserList = "virtualmachineinstances/userlist" | ||
@@ -128,7 +128,7 @@ func newAdminClusterRole() *rbacv1.ClusterRole { | ||
Kind: "ClusterRole", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "kubevirt.io:admin", | ||
+ Name: "kubevirt.internal.virtualization.deckhouse.io:admin", | ||
Labels: map[string]string{ | ||
virtv1.AppLabel: "", | ||
"rbac.authorization.k8s.io/aggregate-to-admin": "true", | ||
@@ -307,7 +307,7 @@ func newEditClusterRole() *rbacv1.ClusterRole { | ||
Kind: "ClusterRole", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "kubevirt.io:edit", | ||
+ Name: "kubevirt.internal.virtualization.deckhouse.io:edit", | ||
Labels: map[string]string{ | ||
virtv1.AppLabel: "", | ||
"rbac.authorization.k8s.io/aggregate-to-edit": "true", | ||
@@ -497,7 +497,7 @@ func newViewClusterRole() *rbacv1.ClusterRole { | ||
Kind: "ClusterRole", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "kubevirt.io:view", | ||
+ Name: "kubevirt.internal.virtualization.deckhouse.io:view", | ||
Labels: map[string]string{ | ||
virtv1.AppLabel: "", | ||
"rbac.authorization.k8s.io/aggregate-to-view": "true", | ||
diff --git a/pkg/virt-operator/resource/generate/rbac/operator.go b/pkg/virt-operator/resource/generate/rbac/operator.go | ||
index 4eca946a4..061135fd9 100644 | ||
--- a/pkg/virt-operator/resource/generate/rbac/operator.go | ||
+++ b/pkg/virt-operator/resource/generate/rbac/operator.go | ||
@@ -435,7 +435,7 @@ func newOperatorRoleBinding(namespace string) *rbacv1.RoleBinding { | ||
Kind: "RoleBinding", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
- Name: "kubevirt-operator-rolebinding", | ||
+ Name: components.OperatorServiceAccountName, | ||
Namespace: namespace, | ||
Labels: map[string]string{ | ||
virtv1.AppLabel: "", |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.