Skip to content

Commit

Permalink
Merge pull request #4 from mikenairn/fix_make_bundle
Browse files Browse the repository at this point in the history
Add Tests and CI config
  • Loading branch information
mikenairn authored Feb 2, 2024
2 parents b81ed4b + b529f3a commit b4a1d8c
Show file tree
Hide file tree
Showing 8 changed files with 411 additions and 10 deletions.
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

0 comments on commit b4a1d8c

Please sign in to comment.