Skip to content

Commit

Permalink
feat: improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
eminetto committed Oct 5, 2023
1 parent 460ed7d commit cafa428
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 84 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ vet: ## Run go vet against code.

.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" ACK_GINKGO_DEPRECATIONS=1.16.5 go test ./... -coverprofile cover.out
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out

##@ Build

Expand Down
87 changes: 38 additions & 49 deletions controllers/application_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"time"

minettodevv1alpha1 "github.com/eminetto/k8s-operator-talk/api/v1alpha1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
Expand All @@ -15,36 +15,35 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

var _ = Describe("Application controller", func() {
// Define utility constants for object names and testing timeouts/durations and intervals.
const (
ApplicationName = "test-app"
ApplicationNamespace = "test-app"
// Define utility constants for object names and testing timeouts/durations and intervals.
const (
ApplicationName = "test-app"
ApplicationNamespace = "test-app"

timeout = time.Second * 10
duration = time.Second * 10
interval = time.Millisecond * 250
)
timeout = time.Second * 10
duration = time.Second * 10
interval = time.Millisecond * 250
)

var _ = Describe("Application controller", func() {
Context("When creating an Application", func() {
It("Should create a Deployment and a Service", func() {
By("An empty cluster")
ctx := context.Background()

// we need to create a namespace
// precisamos criar uma namespace no cluster
ns := corev1.Namespace{
ObjectMeta: v1.ObjectMeta{Name: ApplicationNamespace},
}
Expect(k8sClient.Create(ctx, &ns)).Should(Succeed())

//definimos uma Application
app := minettodevv1alpha1.Application{
TypeMeta: v1.TypeMeta{
Kind: "Application",
APIVersion: "v1alpha1",
},
ObjectMeta: v1.ObjectMeta{
Name: ApplicationName,
// GenerateName: ApplicationNamespace,
Name: ApplicationName,
Namespace: ApplicationNamespace,
},
Spec: minettodevv1alpha1.ApplicationSpec{
Expand All @@ -53,38 +52,33 @@ var _ = Describe("Application controller", func() {
Port: 80,
},
}
//adicionamos o finalizer, conforme descrito no post anterior
controllerutil.AddFinalizer(&app, finalizer)
//garantimos que a criação não teve erros
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
//garantimos que o finalizer foi criado com sucesso
Expect(controllerutil.ContainsFinalizer(&app, finalizer)).Should(BeTrue())

lookupKey := types.NamespacedName{Name: ApplicationName, Namespace: ApplicationNamespace}
createdApp := &minettodevv1alpha1.Application{}

// We'll need to retry getting this newly created Application, given that creation may not immediately happen.
Eventually(func() bool {
err := k8sClient.Get(ctx, lookupKey, createdApp)
if err != nil {
return false
}
return true
}, timeout, interval).Should(BeTrue())
Expect(createdApp.Spec.Image).Should(Equal("nginx:latest"))

// We'll need to retry getting this newly created Deployment, given that creation may not immediately happen.
// vamos agora verificar se o deployment foi criado com sucesso
deplName := types.NamespacedName{Name: app.ObjectMeta.Name + "-deployment", Namespace: app.ObjectMeta.Name}
createdDepl := &appsv1.Deployment{}

//devido a natureza assíncrona do Kubernetes vamos fazer uso da função Eventually do Ginkgo
//ele vai executar a função de acordo com o valor do intervalo, até que o timeout tenha terminado,
//ou o resultado seja true
Eventually(func() bool {
err := k8sClient.Get(ctx, deplName, createdDepl)
if err != nil {
return false
}
return true
}, timeout, interval).Should(BeTrue())
//vamos verificar se os dados do Deployment foram criados de acordo com o esperado
Expect(createdDepl.Spec.Template.Spec.Containers[0].Image).Should(Equal(app.Spec.Image))
//o Application deve ser o Owner do Deployment
Expect(createdDepl.ObjectMeta.OwnerReferences[0].Name).Should(Equal(app.Name))

// We'll need to retry getting this newly created Deployment, given that creation may not immediately happen.
// vamos fazer o mesmo com o Service, garantindo que o controller criou conforme o esperado
srvName := types.NamespacedName{Name: app.ObjectMeta.Name + "-service", Namespace: app.ObjectMeta.Name}
createdSrv := &corev1.Service{}

Expand All @@ -102,9 +96,9 @@ var _ = Describe("Application controller", func() {

Context("When updating an Application", func() {
It("Should update the Deployment", func() {
By("An Application change")
ctx := context.Background()

//vamos primeiro buscar a Application no cluster
appName := types.NamespacedName{Name: ApplicationName, Namespace: ApplicationNamespace}
app := minettodevv1alpha1.Application{}
Eventually(func() bool {
Expand All @@ -115,7 +109,7 @@ var _ = Describe("Application controller", func() {
return true
}, timeout, interval).Should(BeTrue())

// We'll need to retry getting this newly created Deployment, given that creation may not immediately happen.
// vamos buscar o Deployment para garantir que os dados estão iguais aos do Application
deplName := types.NamespacedName{Name: app.ObjectMeta.Name + "-deployment", Namespace: app.ObjectMeta.Name}
createdDepl := &appsv1.Deployment{}

Expand All @@ -128,50 +122,45 @@ var _ = Describe("Application controller", func() {
}, timeout, interval).Should(BeTrue())
Expect(createdDepl.Spec.Template.Spec.Containers[0].Image).Should(Equal(app.Spec.Image))

//vamos alterar a Application
app.Spec.Image = "caddy:latest"
Expect(k8sClient.Update(ctx, &app)).Should(Succeed())

Eventually(func() bool {
err := k8sClient.Get(ctx, appName, &app)
if err != nil {
return false
}
return true
}, timeout, interval).Should(BeTrue())
Expect(app.Spec.Image).Should(Equal("caddy:latest"))

//vamos conferir se a alteração no Application se refletiu no Deployment
Eventually(func() bool {
err := k8sClient.Get(ctx, deplName, createdDepl)
if err != nil {
return false
}
return true
if createdDepl.Spec.Template.Spec.Containers[0].Image == "caddy:latest" {
return true
}
return false
}, timeout, interval).Should(BeTrue())
Expect(createdDepl.Spec.Template.Spec.Containers[0].Image).Should(Equal(app.Spec.Image))
})
})

Context("When deleting an Application", func() {
It("Should delete the Deployment and Service", func() {
// Delete
appName := types.NamespacedName{Name: ApplicationName, Namespace: ApplicationNamespace}
By("Expecting to delete successfully")
//verifica se a exlusão aconteceu com sucesso
Eventually(func() error {
a := &minettodevv1alpha1.Application{}
k8sClient.Get(context.Background(), appName, a)
return k8sClient.Delete(context.Background(), a)
}, timeout, interval).Should(Succeed())

By("Expecting to delete finish")
//garante que o Application não existe mais no cluster
//este teste não é realmente necessário, pois o Delete aconteceu com sucesso
//mantive este teste aqui apenas para fins didáticos
Eventually(func() error {
a := &minettodevv1alpha1.Application{}
return k8sClient.Get(context.Background(), appName, a)
}, timeout, interval).ShouldNot(Succeed())

// According with this documentation: https://book.kubebuilder.io/reference/envtest.html#testing-considerations
// we can't test the garbage collection, to garantee that the created Deployment and Service was removed
// but in the lines 85 e 99 we test the ownership, so everthing will be removed properly

// de acordo com esta documentação : https://book.kubebuilder.io/reference/envtest.html#testing-considerations
// não podemos testar o garbage collection do cluster, para garantir que o Deployment e o Service criados foram removidos
// mas no primeiro teste nós verificamos o ownership, então eles serão removidos de acordo com o esperado em um cluster real
})
})
})
9 changes: 3 additions & 6 deletions controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ import (
"path/filepath"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"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/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

Expand All @@ -50,9 +49,7 @@ var (
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)

RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
RunSpecs(t, "Controller Suite")
}

var _ = BeforeSuite(func() {
Expand Down Expand Up @@ -97,7 +94,7 @@ var _ = BeforeSuite(func() {
err = k8sManager.Start(ctx)
Expect(err).ToNot(HaveOccurred(), "failed to run manager")
}()
}, 60)
})

var _ = AfterSuite(func() {
cancel()
Expand Down
28 changes: 16 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ go 1.21

require (
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.17.0
github.com/onsi/ginkgo/v2 v2.12.1
github.com/onsi/gomega v1.27.10
k8s.io/api v0.23.5
k8s.io/apimachinery v0.23.5
k8s.io/client-go v0.23.5
sigs.k8s.io/controller-runtime v0.11.2
Expand All @@ -24,13 +26,15 @@ require (
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v1.2.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/zapr v1.2.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/imdario/mergo v0.3.12 // indirect
Expand All @@ -48,21 +52,21 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.12.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/api v0.23.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.23.5 // indirect
k8s.io/component-base v0.23.5 // indirect
k8s.io/klog/v2 v2.30.0 // indirect
Expand Down

0 comments on commit cafa428

Please sign in to comment.