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

Add Tests and CI config #4

Merged
merged 1 commit into from
Feb 2, 2024
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
26 changes: 19 additions & 7 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: v1.21.x
cache: false
Expand All @@ -42,7 +42,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.21.x
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false
Expand All @@ -57,7 +57,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.21.x
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false
Expand All @@ -72,7 +72,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.21.x
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false
Expand All @@ -87,7 +87,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.21.x
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false
Expand All @@ -102,10 +102,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: v1.21
cache: false
- name: Run suite
run: |
make test
make test-unit
integration_test_suite:
name: Integration Test Suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: v1.21
cache: false
- name: Run suite
run: |
make test-integration
30 changes: 28 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,15 @@ imports: openshift-goimports ## Run openshift goimports against code.
$(OPENSHIFT_GOIMPORTS) -m github.com/kuadrant/kuadrant-dns-operator -i github.com/kuadrant/kuadrant-operator

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

.PHONY: test-unit
test-unit: manifests generate fmt vet ## Run unit tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -tags=unit -coverprofile cover-unit.out

.PHONY: test-integration
test-integration: manifests generate fmt vet envtest ## Run integration tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./internal/controller... -tags=integration -coverprofile cover-integration.out

.PHONY: local-setup
local-setup: $(KIND) ## Setup local development kind cluster and dependencies
Expand Down Expand Up @@ -206,12 +213,14 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
OPENSHIFT_GOIMPORTS ?= $(LOCALBIN)/openshift-goimports
KIND = $(LOCALBIN)/kind
ACT = $(LOCALBIN)/act

## Tool Versions
KUSTOMIZE_VERSION ?= v5.0.1
CONTROLLER_TOOLS_VERSION ?= v0.12.0
OPENSHIFT_GOIMPORTS_VERSION ?= c70783e636f2213cac683f6865d88c5edace3157
KIND_VERSION = v0.20.0
ACT_VERSION = latest

.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
Expand Down Expand Up @@ -260,12 +269,29 @@ kind: $(KIND) ## Download kind locally if necessary.
$(KIND): $(LOCALBIN)
GOBIN=$(LOCALBIN) go install sigs.k8s.io/kind@$(KIND_VERSION)

.PHONY: act
act: $(ACT)
$(ACT): $(LOCALBIN) ## Download act locally if necessary.
GOBIN=$(LOCALBIN) go install github.com/nektos/act@$(ACT_VERSION)

.PHONY: bundle
bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files.
$(OPERATOR_SDK) generate kustomize manifests -q
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
$(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS)
$(OPERATOR_SDK) bundle validate ./bundle
$(MAKE) bundle-ignore-createdAt

# Since operator-sdk 1.26.0, `make bundle` changes the `createdAt` field from the bundle
# even if it is patched:
# https://github.com/operator-framework/operator-sdk/pull/6136
# This code checks if only the createdAt field. If is the only change, it is ignored.
# Else, it will do nothing.
# https://github.com/operator-framework/operator-sdk/issues/6285#issuecomment-1415350333
# https://github.com/operator-framework/operator-sdk/issues/6285#issuecomment-1532150678
.PHONY: bundle-ignore-createdAt
bundle-ignore-createdAt:
git diff --quiet -I'^ createdAt: ' ./bundle && git checkout ./bundle || true

.PHONY: bundle-build
bundle-build: ## Build the bundle image.
Expand Down
98 changes: 98 additions & 0 deletions internal/controller/dnsheathcheckprobe_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//go:build integration

package controller

import (
"context"
"fmt"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kuadrant/kuadrant-dns-operator/api/v1alpha1"
)

var _ = Describe("DNSHealthCheckProbe controller", func() {
const (
ProbeName = "test-probe"
ProbeNamespace = "default"

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

Context("When creating DNSHealthCheckProbe", func() {
It("Should update health status to healthy", func() {
By("Performing health check")

ctx := context.Background()
probeObj := &v1alpha1.DNSHealthCheckProbe{
ObjectMeta: metav1.ObjectMeta{
Name: ProbeName,
Namespace: ProbeNamespace,
},
Spec: v1alpha1.DNSHealthCheckProbeSpec{
Host: "localhost",
Address: "0.0.0.0",
Port: 3333,
Interval: metav1.Duration{Duration: time.Second * 10},
Path: "/healthy",
},
}

Expect(k8sClient.Create(ctx, probeObj)).Should(Succeed())

Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(probeObj), probeObj)
if err != nil {
return err
}
if probeObj.Status.LastCheckedAt.Time == (time.Time{}) {
return fmt.Errorf("expected probeObj.Status.LastCheckedAt to be non-zero %s, %s", probeObj.Status.LastCheckedAt.Time, (metav1.Time{}).Time)
}
return nil
}, timeout+(time.Second*20), interval).Should(BeNil())

GinkgoWriter.Print(probeObj)

Expect(*probeObj.Status.Healthy).Should(BeTrue())
Expect(probeObj.Status.LastCheckedAt).Should(Not(BeZero()))
})
It("Should update health status to unhealthy", func() {
By("Updating to unhealthy endpoint")

ctx := context.Background()
probeObj := &v1alpha1.DNSHealthCheckProbe{}

err := k8sClient.Get(ctx, client.ObjectKey{
Name: ProbeName,
Namespace: ProbeNamespace,
}, probeObj)
Expect(err).NotTo(HaveOccurred())

patch := client.MergeFrom(probeObj.DeepCopy())
lastUpdate := probeObj.Status.LastCheckedAt
probeObj.Spec.Path = "/unhealthy"
Expect(k8sClient.Patch(ctx, probeObj, patch)).To(BeNil())

Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(probeObj), probeObj)
if err != nil {
return err
}
if !probeObj.Status.LastCheckedAt.Time.After(lastUpdate.Time) {
return fmt.Errorf("expected probeObj.Status.LastCheckedAt to be after lastUpdate")
}
return nil
}, timeout+(time.Second*20), interval).Should(BeNil())

Expect(*probeObj.Status.Healthy).Should(BeFalse())
Expect(probeObj.Status.Reason).Should(Equal("Status code: 500"))
})
})
})
48 changes: 48 additions & 0 deletions internal/controller/helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//go:build integration

package controller

import (
"context"
"fmt"
"net/http"
"time"
)

const (
TestTimeoutMedium = time.Second * 10
TestTimeoutLong = time.Second * 30
TestRetryIntervalMedium = time.Millisecond * 250
defaultNS = "default"
providerCredential = "secretname"
)

type testHealthServer struct {
Port int
}

func (s *testHealthServer) Start(ctx context.Context) error {
mux := http.NewServeMux()

endpoint := func(expectedCode int) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(expectedCode)
}
}

mux.HandleFunc("/healthy", endpoint(200))
mux.HandleFunc("/unhealthy", endpoint(500))

errCh := make(chan error)

go func() {
errCh <- http.ListenAndServe(fmt.Sprintf(":%d", s.Port), mux)
}()

select {
case <-ctx.Done():
return ctx.Err()
case err := <-errCh:
return err
}
}
104 changes: 104 additions & 0 deletions internal/controller/managedzone_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//go:build integration

/*
Copyright 2024.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kuadrant/kuadrant-dns-operator/api/v1alpha1"
//+kubebuilder:scaffold:imports
)

var _ = Describe("ManagedZoneReconciler", func() {
Context("testing ManagedZone controller", func() {
var managedZone *v1alpha1.ManagedZone
var ctx context.Context

BeforeEach(func() {
ctx = context.Background()
managedZone = &v1alpha1.ManagedZone{
ObjectMeta: metav1.ObjectMeta{
Name: "example.com",
Namespace: defaultNS,
},
Spec: v1alpha1.ManagedZoneSpec{
ID: "example.com",
DomainName: "example.com",
SecretRef: v1alpha1.ProviderRef{
Name: providerCredential,
},
},
}
})

AfterEach(func() {
// Clean up managedZones
mzList := &v1alpha1.ManagedZoneList{}
err := k8sClient.List(ctx, mzList, client.InNamespace(defaultNS))
Expect(err).NotTo(HaveOccurred())
for _, mz := range mzList.Items {
err = k8sClient.Delete(ctx, &mz)
Expect(client.IgnoreNotFound(err)).NotTo(HaveOccurred())
}
})

It("should accept a managed zone for this controller and allow deletion", func() {
Expect(k8sClient.Create(ctx, managedZone)).To(BeNil())

createdMZ := &v1alpha1.ManagedZone{}

Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Namespace: managedZone.Namespace, Name: managedZone.Name}, createdMZ)
}, TestTimeoutMedium, TestRetryIntervalMedium).ShouldNot(HaveOccurred())

Expect(k8sClient.Delete(ctx, managedZone)).To(BeNil())

Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: managedZone.Namespace, Name: managedZone.Name}, createdMZ)
if err != nil && !errors.IsNotFound(err) {
return err
}
return nil
}, TestTimeoutMedium, TestRetryIntervalMedium).Should(BeNil())
})

It("should reject a managed zone with an invalid domain name", func() {
invalidDomainNameManagedZone := &v1alpha1.ManagedZone{
ObjectMeta: metav1.ObjectMeta{
Name: "invalid_domain",
Namespace: defaultNS,
},
Spec: v1alpha1.ManagedZoneSpec{
ID: "invalid_domain",
DomainName: "invalid_domain",
},
}
err := k8sClient.Create(ctx, invalidDomainNameManagedZone)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("spec.domainName in body should match"))
})
})
})
Loading
Loading