func (ts *Suite) SanitizeNameForNS(name string) string {
	return InvalidNSNameCharacters.ReplaceAllString(strings.ToLower(name), "")
}
SanitizeNameForNS first converts the given name to lowercase using strings.ToLower
and then remove all characters but `a-z` (only lower case), `0-9` and the `-` (dash).
package envtest
import (
	"context"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
	"github.com/go-logr/logr"
	"github.com/go-logr/zapr"
	"github.com/stretchr/testify/suite"
	"go.uber.org/zap/zaptest"
	appsv1 "k8s.io/api/apps/v1"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/rand"
	"k8s.io/apimachinery/pkg/util/validation"
	"k8s.io/client-go/rest"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/envtest"
	"sigs.k8s.io/controller-runtime/pkg/log"
	k8upv1 "github.com/k8up-io/k8up/api/v1"
	"github.com/k8up-io/k8up/operator/cfg"
	"github.com/k8up-io/k8up/operator/executor"
)
var InvalidNSNameCharacters = regexp.MustCompile("[^a-z0-9-]")
type Suite struct {
	suite.Suite
	NS     string
	Client client.Client
	Config *rest.Config
	Env    *envtest.Environment
	Logger logr.Logger
	Ctx    context.Context
	Scheme *runtime.Scheme
}
func (ts *Suite) SetupSuite() {
	ts.Logger = zapr.NewLogger(zaptest.NewLogger(ts.T()))
	log.SetLogger(ts.Logger)
	cfg.Config = defaultConfiguration()
	ts.Ctx = context.Background()
	envtestAssets, ok := os.LookupEnv("KUBEBUILDER_ASSETS")
	if !ok {
		ts.FailNow("The environment variable KUBEBUILDER_ASSETS is undefined. Configure your IDE to set this variable when running the integration test.")
	}
	info, err := os.Stat(envtestAssets)
	absEnvtestAssets, _ := filepath.Abs(envtestAssets)
	ts.Require().NoErrorf(err, "'%s' does not seem to exist. Check KUBEBUILDER_ASSETS and make sure you run `make integration-test` before you run this test in your IDE.", absEnvtestAssets)
	ts.Require().Truef(info.IsDir(), "'%s' does not seem to be a directory. Check KUBEBUILDER_ASSETS and make sure you run `make integration-test` before you run this test in your IDE.", absEnvtestAssets)
	crds := filepath.Join(Root, "config", "crd", "apiextensions.k8s.io", "v1", "base")
	absCrds, _ := filepath.Abs(crds)
	info, err = os.Stat(crds)
	ts.Require().NoErrorf(err, "'%s' does not seem to exist. Make sure to set the working directory to the project root.", absCrds)
	ts.Require().Truef(info.IsDir(), "'%s' does not seem to be a directory. Make sure to set the working directory to the project root.", absCrds)
	ts.Logger.Info("envtest directories", "crd", absCrds, "binary assets", absEnvtestAssets)
	testEnv := &envtest.Environment{
		ErrorIfCRDPathMissing: true,
		CRDDirectoryPaths:     []string{crds},
		BinaryAssetsDirectory: envtestAssets,
	}
	config, err := testEnv.Start()
	ts.Require().NoError(err)
	ts.Require().NotNil(config)
	registerCRDs(ts)
	k8sClient, err := client.New(config, client.Options{
		Scheme: ts.Scheme,
	})
	ts.Require().NoError(err)
	ts.Require().NotNil(k8sClient)
	executor.GetExecutor()
	ts.Env = testEnv
	ts.Config = config
	ts.Client = k8sClient
}
func registerCRDs(ts *Suite) {
	ts.Scheme = runtime.NewScheme()
	ts.Require().NoError(appsv1.AddToScheme(ts.Scheme))
	ts.Require().NoError(batchv1.AddToScheme(ts.Scheme))
	ts.Require().NoError(corev1.AddToScheme(ts.Scheme))
	ts.Require().NoError(k8upv1.AddToScheme(ts.Scheme))
	ts.Require().NoError(rbacv1.AddToScheme(ts.Scheme))
}
func (ts *Suite) TearDownSuite() {
	err := ts.Env.Stop()
	ts.Require().NoErrorf(err, "error while stopping test environment")
	ts.Logger.Info("test environment stopped")
}
type AssertFunc func(timedCtx context.Context) (done bool, err error)
func (ts *Suite) RepeatedAssert(timeout time.Duration, interval time.Duration, failureMsg string, assertFunc AssertFunc) {
	timedCtx, cancel := context.WithTimeout(ts.Ctx, timeout)
	defer cancel()
	i := 0
	for {
		select {
		case <-time.After(interval):
			i++
			done, err := assertFunc(timedCtx)
			ts.Require().NoError(err)
			if done {
				return
			}
		case <-timedCtx.Done():
			if failureMsg == "" {
				failureMsg = timedCtx.Err().Error()
			}
			ts.FailNowf(failureMsg, "Failed after %s (%d attempts)", timeout, i)
			return
		}
	}
}
func (ts *Suite) NewNS(nsName string) *corev1.Namespace {
	ts.Assert().Emptyf(validation.IsDNS1123Label(nsName), "'%s' does not appear to be a valid name for a namespace", nsName)
	return &corev1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: nsName,
		},
	}
}
func (ts *Suite) EnsureNS(nsName string) {
	ns := ts.NewNS(nsName)
	ts.T().Logf("creating namespace '%s'", nsName)
	ts.Require().NoError(ts.Client.Create(ts.Ctx, ns))
}
func (ts *Suite) EnsureResources(resources ...client.Object) {
	for _, resource := range resources {
		ts.T().Logf("creating resource '%s/%s'", resource.GetNamespace(), resource.GetName())
		ts.Require().NoError(ts.Client.Create(ts.Ctx, resource))
	}
}
func (ts *Suite) UpdateResources(resources ...client.Object) {
	for _, resource := range resources {
		ts.T().Logf("updating resource '%s/%s'", resource.GetNamespace(), resource.GetName())
		ts.Require().NoError(ts.Client.Update(ts.Ctx, resource))
	}
}
func (ts *Suite) UpdateStatus(resources ...client.Object) {
	for _, resource := range resources {
		ts.T().Logf("updating status '%s/%s'", resource.GetNamespace(), resource.GetName())
		ts.Require().NoError(ts.Client.Status().Update(ts.Ctx, resource))
	}
}
func (ts *Suite) SetCondition(
	resource client.Object,
	conditions *[]metav1.Condition,
	cType k8upv1.ConditionType,
	status metav1.ConditionStatus,
	reason k8upv1.ConditionReason) {
	meta.SetStatusCondition(conditions, metav1.Condition{
		Type:    cType.String(),
		Status:  status,
		Reason:  reason.String(),
		Message: reason.String(),
	})
	ts.UpdateStatus(resource)
}
func (ts *Suite) DeleteResources(resources ...client.Object) {
	for _, resource := range resources {
		ts.T().Logf("deleting '%s/%s'", resource.GetNamespace(), resource.GetName())
		ts.Require().NoError(ts.Client.Delete(ts.Ctx, resource))
	}
}
func (ts *Suite) FetchResource(name types.NamespacedName, object client.Object) {
	ts.Require().NoError(ts.Client.Get(ts.Ctx, name, object))
}
func (ts *Suite) FetchResources(objectList client.ObjectList, opts ...client.ListOption) {
	ts.Require().NoError(ts.Client.List(ts.Ctx, objectList, opts...))
}
func (ts *Suite) MapToRequest(object metav1.Object) ctrl.Request {
	return ctrl.Request{
		NamespacedName: types.NamespacedName{
			Name:      object.GetName(),
			Namespace: object.GetNamespace(),
		},
	}
}
func (ts *Suite) SetupTest() {
	ts.NS = rand.String(8)
	ts.EnsureNS(ts.NS)
}