Skip to content

Commit

Permalink
Merge pull request #41 from snapp-incubator/add/grafauser-webhook
Browse files Browse the repository at this point in the history
add/grafanauser-webhook
  • Loading branch information
Sinamcp authored Aug 4, 2022
2 parents 075451b + f65c9be commit df82942
Show file tree
Hide file tree
Showing 29 changed files with 481 additions and 64 deletions.
8 changes: 6 additions & 2 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ resources:
namespaced: true
controller: true
domain: snappcloud.io
group: grafanauser
group: grafana
kind: GrafanaUser
path: github.com/snapp-cab/grafana-complementary-operator/apis/grafanauser/v1alpha1
path: github.com/snapp-cab/grafana-complementary-operator/apis/grafana/v1alpha1
version: v1alpha1
webhooks:
defaulting: true
validation: true
webhookVersion: v1
version: "3"
File renamed without changes.
126 changes: 126 additions & 0 deletions apis/grafana/v1alpha1/grafanauser_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Copyright 2022.
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"
"fmt"
"os"
"strings"

"github.com/grafana-tools/sdk"
"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"
)

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

// Get Grafana URL and PassWord as a env.
var grafanaPassword = os.Getenv("GRAFANA_PASSWORD")
var grafanaUsername = os.Getenv("GRAFANA_USERNAME")
var grafanaURL = os.Getenv("GRAFANA_URL")

// Get Grafana URL and PassWord as a env.

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

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

var _ webhook.Defaulter = &GrafanaUser{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *GrafanaUser) Default() {
grafanauserlog.Info("default", "name", r.Name)

// TODO(user): fill in your defaulting logic.
}

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
//+kubebuilder:webhook:path=/validate-grafana-snappcloud-io-v1alpha1-grafanauser,mutating=false,failurePolicy=fail,sideEffects=None,groups=grafana.snappcloud.io,resources=grafanausers,verbs=create;update,versions=v1alpha1,name=vgrafana.kb.io,admissionReviewVersions={v1,v1beta1}

var _ webhook.Validator = &GrafanaUser{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *GrafanaUser) ValidateCreate() error {
grafanauserlog.Info("validate create", "name", r.Name)
// TODO(user): fill in your validation logic upon object creation.
var emaillist []string
emaillist = append(r.Spec.Admin, r.Spec.Edit...)
emaillist = append(emaillist, r.Spec.View...)
str2 := strings.Join(emaillist, ", ")
grafanauserlog.Info(str2)
err := r.ValidateEmailExist(context.Background(), emaillist)
if err != nil {
return err
}
return nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *GrafanaUser) ValidateUpdate(old runtime.Object) error {
grafanauserlog.Info("validate update", "name", r.Name)
var emaillist []string
emaillist = append(r.Spec.Admin, r.Spec.Edit...)
emaillist = append(emaillist, r.Spec.View...)
err := r.ValidateEmailExist(context.Background(), emaillist)
if err != nil {
return err
}
// TODO(user): fill in your validation logic upon object update.
return nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *GrafanaUser) ValidateDelete() error {
grafanauserlog.Info("validate delete", "name", r.Name)
// TODO(user): fill in your validation logic upon object deletion.
return nil
}

func Find(slice []sdk.User, val string) bool {
for _, item := range slice {
if item.Email == val {
return true
}
}
return false
}

func (r *GrafanaUser) ValidateEmailExist(ctx context.Context, emails []string) error {
client, _ := sdk.NewClient(grafanaURL, fmt.Sprintf("%s:%s", grafanaUsername, grafanaPassword), sdk.DefaultHTTPClient)
grafanalUsers, _ := client.GetAllUsers(ctx)
var Users []string
for _, email := range emails {
found := Find(grafanalUsers, email)
if !found {
Users = append(Users, email)
}
}
userlist := strings.Join(Users, ", ")
if len(Users) > 0 {
return fmt.Errorf("%q do NOT exist in grafana, please make sure the user name is correct, or they have login at least one time in grafana and then try again", userlist)

}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package v1alpha1 contains API Schema definitions for the grafanauser v1alpha1 API group
// Package v1alpha1 contains API Schema definitions for the grafana v1alpha1 API group
//+kubebuilder:object:generate=true
//+groupName=grafanauser.snappcloud.io
//+groupName=grafana.snappcloud.io
package v1alpha1

import (
Expand All @@ -26,7 +26,7 @@ import (

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "grafanauser.snappcloud.io", Version: "v1alpha1"}
GroupVersion = schema.GroupVersion{Group: "grafana.snappcloud.io", Version: "v1alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
Expand Down
135 changes: 135 additions & 0 deletions apis/grafana/v1alpha1/webhook_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
Copyright 2022.
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"
. "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"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
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)

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

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 = (&GrafanaUser{}).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())

}, 60)

var _ = AfterSuite(func() {
cancel()
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

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

25 changes: 25 additions & 0 deletions config/certmanager/certificate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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:
name: selfsigned-issuer
namespace: system
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
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
16 changes: 16 additions & 0 deletions config/certmanager/kustomizeconfig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This configuration is for teaching kustomize how to update name ref and var substitution
nameReference:
- kind: Issuer
group: cert-manager.io
fieldSpecs:
- kind: Certificate
group: cert-manager.io
path: spec/issuerRef/name

varReference:
- kind: Certificate
group: cert-manager.io
path: spec/commonName
- kind: Certificate
group: cert-manager.io
path: spec/dnsNames
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: grafanausers.grafanauser.snappcloud.io
name: grafanausers.grafana.snappcloud.io
spec:
group: grafanauser.snappcloud.io
group: grafana.snappcloud.io
names:
kind: GrafanaUser
listKind: GrafanaUserList
Expand Down
6 changes: 3 additions & 3 deletions config/crd/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/grafanauser.snappcloud.io_grafanausers.yaml
- bases/grafana.snappcloud.io_grafanausers.yaml
#+kubebuilder:scaffold:crdkustomizeresource

patchesStrategicMerge:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
#- patches/webhook_in_grafanausers.yaml
- patches/webhook_in_grafana_grafanausers.yaml
#+kubebuilder:scaffold:crdkustomizewebhookpatch

# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- patches/cainjection_in_grafanausers.yaml
- patches/cainjection_in_grafana_grafanausers.yaml
#+kubebuilder:scaffold:crdkustomizecainjectionpatch

# the following config is for teaching kustomize how to do kustomization for CRDs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: grafanausers.grafanauser.snappcloud.io
name: grafanausers.grafana.snappcloud.io
Loading

0 comments on commit df82942

Please sign in to comment.