Skip to content

Commit

Permalink
outgoing-webhooks validation (#178)
Browse files Browse the repository at this point in the history
* outgoing-webhooks validation
  • Loading branch information
OrNovo authored Oct 17, 2024
1 parent 3e400e3 commit 4771b69
Show file tree
Hide file tree
Showing 19 changed files with 580 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
name: Tests
runs-on: ubuntu-latest
env:
IMG: tests.com/coralogix-operator:v0.0.1
IMG: coralogix-operator-image:latest
CORALOGIX_REGION: ${{ secrets.CORALOGIX_REGION }}
CORALOGIX_API_KEY: ${{ secrets.CORALOGIX_API_KEY }}
steps:
Expand Down
30 changes: 17 additions & 13 deletions .github/workflows/integration-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,30 @@ jobs:
tests:
name: Tests
runs-on: ubuntu-latest
env:
IMG: coralogix-operator-image:latest
CORALOGIX_REGION: ${{ secrets.CORALOGIX_REGION }}
CORALOGIX_API_KEY: ${{ secrets.CORALOGIX_API_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build the controller-manager Docker image
run: |
make docker-build
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.4.0
- name: Load the controller-manager image into Kind
run: |
kind load docker-image ${{ env.IMG }} --name chart-testing
- name: Deploy the controller-manager
run: |
make deploy
- name: Install kuttl
run: |
curl -Lo /usr/local/bin/kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v0.12.1/kubectl-kuttl_0.12.1_linux_x86_64
chmod +x /usr/local/bin/kubectl-kuttl
- name: Install CRDs
run: make install
- name: Install Go
uses: actions/setup-go@37335c7bb261b353407cff977110895fa0b4f7d8
with:
go-version: 1.22.x
- name: Running operator and Tests
env:
CORALOGIX_REGION: ${{ secrets.CORALOGIX_REGION }}
CORALOGIX_API_KEY: ${{ secrets.CORALOGIX_API_KEY }}
- name: Run Integration Tests
run: |
go run -ldflags="-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn" main.go &
sleep 30s
make integration-tests
make integration-tests
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.22 as builder
FROM golang:1.23 as builder
ARG TARGETOS
ARG TARGETARCH
ARG LDFLAGS
Expand Down
20 changes: 19 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

# Image URL to use all building/pushing image targets
IMG ?= controller:latest
# Enable Webhooks for the operator
ENABLE_WEBHOOKS ?= false
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.25.0
LDFLAGS ?= "-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn"
Expand Down Expand Up @@ -121,8 +123,24 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified

.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
# Step 1: Set the image in the manager deployment
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build config/default | envsubst |kubectl apply -f -

# Step 2: Export the ENABLE_WEBHOOKS variable to envsubst
export ENABLE_WEBHOOKS=$(ENABLE_WEBHOOKS)

# Step 3: Build and apply the configuration (including namespace) to the cluster
$(KUSTOMIZE) build config/default | envsubst | kubectl apply -f -

# Step 4: Wait for the namespace to be ready
NAMESPACE=$(kubectl get namespace -l app.kubernetes.io/instance=coralogix-operator-system -o=jsonpath='{.items[0].metadata.name}')
while ! kubectl get namespace $$NAMESPACE; do sleep 1; done

# Step 5: If webhooks are enabled, run the certificate generation script
@if [ "$(ENABLE_WEBHOOKS)" = "true" ]; then \
bash config/webhook/setup-webhook-and-certs.sh $$NAMESPACE ; \
fi


.PHONY: undeploy
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
Expand Down
3 changes: 3 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ resources:
kind: OutboundWebhook
path: coralogix-operator/apis/coralogix/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
version: "3"
104 changes: 104 additions & 0 deletions apis/coralogix/v1alpha1/outboundwebhook_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
Copyright 2023.
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 v1alpha1

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// log is for logging in this package.
var outboundwebhooklog = logf.Log.WithName("outboundwebhook-resource")

func (r *OutboundWebhook) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

//+kubebuilder:webhook:path=/validate-coralogix-com-v1alpha1-outboundwebhook,mutating=false,failurePolicy=fail,sideEffects=None,groups=coralogix.com,resources=outboundwebhooks,verbs=create;update,versions=v1alpha1,name=outboundwebhook.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &OutboundWebhook{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *OutboundWebhook) ValidateCreate() (warnings admission.Warnings, err error) {
return validateWebhookType(r.Spec.OutboundWebhookType)
}

func validateWebhookType(webhookType OutboundWebhookType) (admission.Warnings, error) {
webhookTypes := webhookTypesBeingSet(webhookType)
if len(webhookTypes) == 0 {
return admission.Warnings{"at least one webhook type should be set"}, fmt.Errorf("at least one webhook type should be set")
}

if len(webhookTypes) > 1 {
return admission.Warnings{"only one webhook type should be set"}, fmt.Errorf("only one webhook type should be set, but got: %v", webhookTypes)
}

return nil, nil
}

func webhookTypesBeingSet(webhookType OutboundWebhookType) []string {
var typesSet []string
if webhookType.GenericWebhook != nil {
typesSet = append(typesSet, "GenericWebhook")
}
if webhookType.Opsgenie != nil {
typesSet = append(typesSet, "Opsgenie")
}
if webhookType.Slack != nil {
typesSet = append(typesSet, "Slack")
}
if webhookType.SendLog != nil {
typesSet = append(typesSet, "SendLog")
}
if webhookType.EmailGroup != nil {
typesSet = append(typesSet, "EmailGroup")
}
if webhookType.MicrosoftTeams != nil {
typesSet = append(typesSet, "MicrosoftTeams")
}
if webhookType.PagerDuty != nil {
typesSet = append(typesSet, "PagerDuty")
}
if webhookType.Jira != nil {
typesSet = append(typesSet, "Jira")
}
if webhookType.Demisto != nil {
typesSet = append(typesSet, "Demisto")
}
if webhookType.AwsEventBridge != nil {
typesSet = append(typesSet, "AwsEventBridge")
}

return typesSet
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *OutboundWebhook) ValidateUpdate(_ runtime.Object) (warnings admission.Warnings, err error) {
return validateWebhookType(r.Spec.OutboundWebhookType)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *OutboundWebhook) ValidateDelete() (warnings admission.Warnings, err error) {
return nil, nil
}
132 changes: 132 additions & 0 deletions apis/coralogix/v1alpha1/webhook_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
Copyright 2023.
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 v1alpha1

import (
"context"
"crypto/tls"
"fmt"
"net"
"path/filepath"
"testing"
"time"

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

admissionv1beta1 "k8s.io/api/admission/v1beta1"
//+kubebuilder:scaffold:imports
"k8s.io/apimachinery/pkg/runtime"
"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"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)

// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.

var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var ctx context.Context
var cancel context.CancelFunc

func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)

RunSpecs(t, "Webhook Suite")
}

var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))

ctx, cancel = context.WithCancel(context.TODO())

By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: false,
WebhookInstallOptions: envtest.WebhookInstallOptions{
Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
},
}

var err error
// cfg is defined in this file globally.
cfg, err = testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())

scheme := runtime.NewScheme()
err = AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())

err = admissionv1beta1.AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:scheme

k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())

// start webhook server using Manager
webhookInstallOptions := &testEnv.WebhookInstallOptions
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme,
Host: webhookInstallOptions.LocalServingHost,
Port: webhookInstallOptions.LocalServingPort,
CertDir: webhookInstallOptions.LocalServingCertDir,
LeaderElection: false,
MetricsBindAddress: "0",
})
Expect(err).NotTo(HaveOccurred())

err = (&OutboundWebhook{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:webhook

go func() {
defer GinkgoRecover()
err = mgr.Start(ctx)
Expect(err).NotTo(HaveOccurred())
}()

// wait for the webhook server to get ready
dialer := &net.Dialer{Timeout: time.Second}
addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
Eventually(func() error {
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return err
}
conn.Close()
return nil
}).Should(Succeed())

})

var _ = AfterSuite(func() {
cancel()
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})
39 changes: 39 additions & 0 deletions config/certmanager/certificate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# The following manifests contain a self-signed issuer CR and a certificate CR.
# More document can be found at https://docs.cert-manager.io
# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
labels:
app.kubernetes.io/name: issuer
app.kubernetes.io/instance: selfsigned-issuer
app.kubernetes.io/component: certificate
app.kubernetes.io/created-by: coralogix-operator
app.kubernetes.io/part-of: coralogix-operator
app.kubernetes.io/managed-by: kustomize
name: selfsigned-issuer
namespace: system
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
labels:
app.kubernetes.io/name: certificate
app.kubernetes.io/instance: serving-cert
app.kubernetes.io/component: certificate
app.kubernetes.io/created-by: coralogix-operator
app.kubernetes.io/part-of: coralogix-operator
app.kubernetes.io/managed-by: kustomize
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
namespace: system
spec:
# $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
dnsNames:
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
issuerRef:
kind: Issuer
name: selfsigned-issuer
secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize
5 changes: 5 additions & 0 deletions config/certmanager/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resources:
- certificate.yaml

configurations:
- kustomizeconfig.yaml
Loading

0 comments on commit 4771b69

Please sign in to comment.