Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed envtest setup to run multiple instances and added a validation sample test #55

Merged
merged 5 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/v1alpha1/nats_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ type NATSSpec struct {
// Cluster defines configurations that are specific to NATS clusters.
type Cluster struct {
// Size of a NATS cluster, i.e. number of NATS nodes.
// +kubebuilder:validation:XValidation:rule="( self % 2 ) != 0", message="size only accepts odd numbers"
Size int `json:"size"`
}

Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions config/crd/bases/operator.kyma-project.io_nats.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ spec:
size:
description: Size of a NATS cluster, i.e. number of NATS nodes.
type: integer
x-kubernetes-validations:
- message: size only accepts odd numbers
rule: ( self % 2 ) != 0
required:
- size
type: object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nats_test
package controller_test

import (
"os"
Expand All @@ -17,6 +17,8 @@ import (
natsmatchers "github.com/kyma-project/nats-manager/testutils/matchers/nats"
)

const projectRootDir = "../../../../../"

var testEnvironment *integration.TestEnvironment //nolint:gochecknoglobals // used in tests

// TestMain pre-hook and post-hook to run before and after all tests.
Expand All @@ -26,7 +28,7 @@ func TestMain(m *testing.M) {

// setup env test
var err error
testEnvironment, err = integration.NewTestEnvironment()
testEnvironment, err = integration.NewTestEnvironment(projectRootDir, false)
if err != nil {
panic(err)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package validation_test

import (
"os"
"testing"

"github.com/kyma-project/nats-manager/api/v1alpha1"
"github.com/kyma-project/nats-manager/testutils"
"github.com/stretchr/testify/require"

"github.com/kyma-project/nats-manager/testutils/integration"
)

const projectRootDir = "../../../../../"

var testEnvironment *integration.TestEnvironment //nolint:gochecknoglobals // used in tests

// TestMain pre-hook and post-hook to run before and after all tests.
func TestMain(m *testing.M) {
// Note: The setup will provision a single K8s env and
// all the tests need to create and use a separate namespace

// setup env test
var err error
testEnvironment, err = integration.NewTestEnvironment(projectRootDir, true)
if err != nil {
panic(err)
}

// run tests
code := m.Run()

// tear down test env
if err = testEnvironment.TearDown(); err != nil {
panic(err)
}

os.Exit(code)
}

func Test_Validate_CreateNatsCR(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
givenNATS *v1alpha1.NATS
wantErrMsg string
}{
{
name: `a NATS CR with a spec.cluster.size that is even should return an error`,
givenNATS: testutils.NewNATSCR(
testutils.WithNATSCRDefaults(),
testutils.WithNATSClusterSize(4)),
wantErrMsg: "size only accepts odd numbers",
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// given
// create unique namespace for this test run.
givenNamespace := tc.givenNATS.GetNamespace()
testEnvironment.EnsureNamespaceCreation(t, givenNamespace)

// when
err := testEnvironment.CreateK8sResource(tc.givenNATS)

// then
if tc.wantErrMsg == "" {
require.NoError(t, err)
} else {
require.Contains(t, err.Error(), tc.wantErrMsg)
}
})
}
}
36 changes: 25 additions & 11 deletions testutils/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ import (
)

const (
NATSChartDir = "../../../resources/nats"
NATSChartDir = "resources/nats"
useExistingCluster = false
attachControlPlaneOutput = false
testEnvStartDelay = time.Minute
testEnvStartAttempts = 10

TwoMinTimeOut = 120 * time.Second
BigPollingInterval = 3 * time.Second
BigTimeOut = 40 * time.Second
SmallTimeOut = 5 * time.Second
BigTimeOut = 60 * time.Second
SmallTimeOut = 10 * time.Second
SmallPollingInterval = 1 * time.Second

NATSContainerName = "nats"
Expand All @@ -71,7 +71,7 @@ type TestEnvironment struct {
TestCancelFn context.CancelFunc
}

func NewTestEnvironment() (*TestEnvironment, error) { //nolint:funlen // Used in testing.
func NewTestEnvironment(projectRootDir string, celValidationEnabled bool) (*TestEnvironment, error) { //nolint:funlen,lll // Used in testing.
var err error
// setup context
ctx := context.Background()
Expand All @@ -82,7 +82,7 @@ func NewTestEnvironment() (*TestEnvironment, error) { //nolint:funlen // Used in
return nil, err
}

testEnv, envTestKubeCfg, err := StartEnvTest()
testEnv, envTestKubeCfg, err := StartEnvTest(projectRootDir, celValidationEnabled)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -112,8 +112,11 @@ func NewTestEnvironment() (*TestEnvironment, error) { //nolint:funlen // Used in
}

ctrlMgr, err := ctrl.NewManager(envTestKubeCfg, ctrl.Options{
Scheme: scheme.Scheme,
Port: metricsPort,
Scheme: scheme.Scheme,
Port: metricsPort,
MetricsBindAddress: "0", // disable
HealthProbeBindAddress: "0", // disable
PprofBindAddress: "0", // disable
})
if err != nil {
return nil, err
Expand All @@ -128,7 +131,7 @@ func NewTestEnvironment() (*TestEnvironment, error) { //nolint:funlen // Used in
kubeClient := k8s.NewKubeClient(ctrlMgr.GetClient(), apiClientSet, "nats-manager")

// create helmRenderer
helmRenderer, err := chart.NewHelmRenderer(NATSChartDir, sugaredLogger)
helmRenderer, err := chart.NewHelmRenderer(filepath.Join(projectRootDir, NATSChartDir), sugaredLogger)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -194,6 +197,10 @@ func (env TestEnvironment) TearDown() error {
return err
}

func (env TestEnvironment) CreateK8sResource(obj client.Object) error {
return env.k8sClient.Create(env.Context, obj)
}

func (env TestEnvironment) EnsureNamespaceCreation(t *testing.T, namespace string) {
if namespace == "default" {
return
Expand Down Expand Up @@ -585,19 +592,26 @@ func (env TestEnvironment) GetNATSAssert(g *gomega.GomegaWithT,
}, BigTimeOut, SmallPollingInterval)
}

func StartEnvTest() (*envtest.Environment, *rest.Config, error) {
func StartEnvTest(projectRootDir string, celValidationEnabled bool) (*envtest.Environment, *rest.Config, error) {
// Reference: https://book.kubebuilder.io/reference/envtest.html
useExistingCluster := useExistingCluster
testEnv := &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "..", "config", "crd", "bases"),
filepath.Join("..", "..", "..", "config", "crd", "external"),
filepath.Join(projectRootDir, "config", "crd", "bases"),
filepath.Join(projectRootDir, "config", "crd", "external"),
},
ErrorIfCRDPathMissing: true,
AttachControlPlaneOutput: attachControlPlaneOutput,
UseExistingCluster: &useExistingCluster,
}

args := testEnv.ControlPlane.GetAPIServer().Configure()
if celValidationEnabled {
args.Set("feature-gates", "CustomResourceValidationExpressions=true")
} else {
args.Set("feature-gates", "CustomResourceValidationExpressions=false")
}

var cfg *rest.Config
err := retry.Do(func() error {
defer func() {
Expand Down