Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Commit

Permalink
Merge pull request #52 from ruromero/validatingWebhook
Browse files Browse the repository at this point in the history
Validating webhook
  • Loading branch information
font authored Nov 25, 2020
2 parents 8799c50 + 6c1e206 commit 66712a1
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 78 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,31 @@ If you would like to deploy Operator using OLM, you'll need to build and push th
source: gatekeeper-operator
sourceNamespace: gatekeeper-system
```

## Usage

Before using Gatekeeper you have to create a `gatekeeper` resource that will be consumed by the operator and create all the necessary resources for you.

Here you can find an example of a `gatekeeper` resource definition:

```yaml
apiVersion: operator.gatekeeper.sh/v1alpha1
kind: Gatekeeper
metadata:
name: gatekeeper
spec:
# Add fields here
audit:
replicas: 1
logLevel: ERROR
```

If nothing is defined in the `spec`, the default values will be used. In the example above the number of replicas for the audit pod is set to `1` and the logLevel to `ERROR` where the default is `INFO`.

The default behaviour for the `ValidatingWebhookConfiguration` is `ENABLED`, that means that it will be created. To disable the `ValidatingWebhookConfiguration` deployment, set the `validatingWebhook` spec property to `DISABLED`.

In order to create an instance of gatekeeper in the specified namespace you can start from one of the [sample configurations](config/samples).

```shell
kubectl create -f config/samples/operator_v1alpha1_gatekeeper.yaml
```
1 change: 1 addition & 0 deletions api/v1alpha1/gatekeeper_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type GatekeeperSpec struct {
Image *ImageConfig `json:"image,omitempty"`
// +optional
Audit *AuditConfig `json:"audit,omitempty"`
// +kubebuilder:default:=Enabled
// +optional
ValidatingWebhook *WebhookMode `json:"validatingWebhook,omitempty"`
// +optional
Expand Down
1 change: 1 addition & 0 deletions config/crd/bases/operator.gatekeeper.sh_gatekeepers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ spec:
type: object
type: array
validatingWebhook:
default: Enabled
enum:
- Enabled
- Disabled
Expand Down
49 changes: 20 additions & 29 deletions controllers/gatekeeper_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ package controllers

import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"

"github.com/RHsyseng/operator-utils/pkg/utils/openshift"
Expand Down Expand Up @@ -202,7 +200,7 @@ func (r *GatekeeperReconciler) SetupWithManager(mgr ctrl.Manager) error {
}

func (r *GatekeeperReconciler) deployGatekeeperResources(gatekeeper *operatorv1alpha1.Gatekeeper, platformName string) error {
for _, a := range orderedStaticAssets {
for _, a := range getStaticAssets(gatekeeper) {
if a == RoleFile && platformName == "OpenShift" {
a = openshiftAssetsDir + a
}
Expand All @@ -221,6 +219,19 @@ func (r *GatekeeperReconciler) deployGatekeeperResources(gatekeeper *operatorv1a
return nil
}

func getStaticAssets(gatekeeper *operatorv1alpha1.Gatekeeper) []string {
if gatekeeper.Spec.ValidatingWebhook == nil || *gatekeeper.Spec.ValidatingWebhook == operatorv1alpha1.WebhookEnabled {
return orderedStaticAssets
}
assets := make([]string, 0)
for _, a := range orderedStaticAssets {
if a != ValidatingWebhookConfiguration {
assets = append(assets, a)
}
}
return assets
}

func (r *GatekeeperReconciler) updateOrCreateResource(manifest *manifest.Manifest, gatekeeper *operatorv1alpha1.Gatekeeper) error {
var err error
ctx := context.Background()
Expand Down Expand Up @@ -506,7 +517,7 @@ func setFailurePolicy(obj *unstructured.Unstructured, failurePolicy *admregv1.Fa

func setAffinity(obj *unstructured.Unstructured, spec operatorv1alpha1.GatekeeperSpec) error {
if spec.Affinity != nil {
if err := unstructured.SetNestedField(obj.Object, toMap(spec.Affinity), "spec", "template", "spec", "affinity"); err != nil {
if err := unstructured.SetNestedField(obj.Object, util.ToMap(spec.Affinity), "spec", "template", "spec", "affinity"); err != nil {
return errors.Wrapf(err, "Failed to set affinity value")
}
}
Expand Down Expand Up @@ -535,7 +546,7 @@ func setTolerations(obj *unstructured.Unstructured, spec operatorv1alpha1.Gateke
if spec.Tolerations != nil {
tolerations := make([]interface{}, len(spec.Tolerations))
for i, t := range spec.Tolerations {
tolerations[i] = toMap(t)
tolerations[i] = util.ToMap(t)
}
if err := unstructured.SetNestedSlice(obj.Object, tolerations, "spec", "template", "spec", "tolerations"); err != nil {
return errors.Wrapf(err, "Failed to set container tolerations")
Expand All @@ -548,7 +559,7 @@ func setTolerations(obj *unstructured.Unstructured, spec operatorv1alpha1.Gateke

func setResources(container map[string]interface{}, spec operatorv1alpha1.GatekeeperSpec) error {
if spec.Resources != nil {
if err := unstructured.SetNestedField(container, toMap(spec.Resources), "resources"); err != nil {
if err := unstructured.SetNestedField(container, util.ToMap(spec.Resources), "resources"); err != nil {
return errors.Wrapf(err, "Failed to set container resources")
}
}
Expand Down Expand Up @@ -601,35 +612,15 @@ func setContainerArg(obj *unstructured.Unstructured, containerName, argName stri
}
exists := false
for i, arg := range args {
n, _ := fromArg(arg)
n, _ := util.FromArg(arg)
if n == argName {
args[i] = ToArg(argName, argValue)
args[i] = util.ToArg(argName, argValue)
exists = true
}
}
if !exists {
args = append(args, ToArg(argName, argValue))
args = append(args, util.ToArg(argName, argValue))
}
return unstructured.SetNestedStringSlice(container, args, "args")
})
}

// toMap Convenience method to convert any struct into a map
func toMap(obj interface{}) map[string]interface{} {
var result map[string]interface{}
resultRec, _ := json.Marshal(obj)
json.Unmarshal(resultRec, &result)
return result
}

func ToArg(name, value string) string {
return name + "=" + value
}

func fromArg(arg string) (key, value string) {
parts := strings.Split(arg, "=")
if len(parts) == 1 {
return parts[0], ""
}
return parts[0], parts[1]
}
35 changes: 26 additions & 9 deletions controllers/gatekeeper_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ import (
test "github.com/gatekeeper/gatekeeper-operator/test/util"
)

func TestDeployValidatingWebhookConfig(t *testing.T) {
g := NewWithT(t)
gatekeeper := &operatorv1alpha1.Gatekeeper{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "testns",
},
}
g.Expect(getStaticAssets(gatekeeper)).To(ContainElement(ValidatingWebhookConfiguration))
webhookMode := operatorv1alpha1.WebhookEnabled
gatekeeper.Spec.ValidatingWebhook = &webhookMode
g.Expect(getStaticAssets(gatekeeper)).To(ContainElement(ValidatingWebhookConfiguration))
webhookMode = operatorv1alpha1.WebhookDisabled
gatekeeper.Spec.ValidatingWebhook = &webhookMode
g.Expect(getStaticAssets(gatekeeper)).NotTo(ContainElement(ValidatingWebhookConfiguration))
}

func TestReplicas(t *testing.T) {
g := NewWithT(t)
auditReplicaOverride := int32(4)
Expand Down Expand Up @@ -152,7 +169,7 @@ func assertWebhookAffinity(g *WithT, manifest *manifest.Manifest, expected *core
}

func assertAffinity(g *WithT, expected *corev1.Affinity, current interface{}) {
g.Expect(toMap(expected)).To(BeEquivalentTo(toMap(current)))
g.Expect(util.ToMap(expected)).To(BeEquivalentTo(util.ToMap(current)))
}

func TestNodeSelector(t *testing.T) {
Expand Down Expand Up @@ -312,7 +329,7 @@ func assertTolerations(g *WithT, manifest *manifest.Manifest, expected []corev1.
g.Expect(found).To(BeFalse())
} else {
for i, toleration := range expected {
g.Expect(toMap(toleration)).To(BeEquivalentTo(current[i]))
g.Expect(util.ToMap(toleration)).To(BeEquivalentTo(current[i]))
}
}
}
Expand Down Expand Up @@ -369,7 +386,7 @@ func assertResources(g *WithT, manifest *manifest.Manifest, expected *corev1.Res
g.Expect(found).To(BeTrue())

for _, c := range containers {
current, found, err := unstructured.NestedMap(toMap(c), "resources")
current, found, err := unstructured.NestedMap(util.ToMap(c), "resources")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(found).To(BeTrue())
if expected == nil {
Expand Down Expand Up @@ -436,15 +453,15 @@ func assertImage(g *WithT, manifest *manifest.Manifest, expected *operatorv1alph
g.Expect(found).To(BeTrue())

for _, c := range containers {
currentImage, found, err := unstructured.NestedString(toMap(c), "image")
currentImage, found, err := unstructured.NestedString(util.ToMap(c), "image")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(found).To(BeTrue())
if expected == nil {
g.Expect(defaultImage).To(BeEquivalentTo(currentImage))
} else {
g.Expect(*expected.Image).To(BeEquivalentTo(currentImage))
}
currentImagePullPolicy, found, err := unstructured.NestedString(toMap(c), "imagePullPolicy")
currentImagePullPolicy, found, err := unstructured.NestedString(util.ToMap(c), "imagePullPolicy")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(found).To(BeTrue())
if expected == nil {
Expand Down Expand Up @@ -512,7 +529,7 @@ func assertFailurePolicy(g *WithT, manifest *manifest.Manifest, expected *admreg
for _, w := range webhooks {
webhook := w.(map[string]interface{})
if webhook["name"] == ValidationGatekeeperWebhook {
current, found, err := unstructured.NestedString(toMap(w), "failurePolicy")
current, found, err := unstructured.NestedString(util.ToMap(w), "failurePolicy")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(found).To(BeTrue())
if expected == nil {
Expand Down Expand Up @@ -859,16 +876,16 @@ func getContainerArguments(g *WithT, containerName string, manifest *manifest.Ma
g.Expect(found).To(BeTrue())

for _, c := range containers {
cName, found, err := unstructured.NestedString(toMap(c), "name")
cName, found, err := unstructured.NestedString(util.ToMap(c), "name")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(found).To(BeTrue())
if cName == containerName {
args, found, err := unstructured.NestedStringSlice(toMap(c), "args")
args, found, err := unstructured.NestedStringSlice(util.ToMap(c), "args")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(found).To(BeTrue())
argsMap := make(map[string]string)
for _, arg := range args {
key, value := fromArg(arg)
key, value := util.FromArg(arg)
argsMap[key] = value
}
return argsMap
Expand Down
64 changes: 32 additions & 32 deletions pkg/bindata/bindata.go

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

25 changes: 25 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ limitations under the License.
package util

import (
"encoding/json"
"strings"

"github.com/openshift/library-go/pkg/manifest"
"github.com/pkg/errors"

Expand All @@ -39,3 +42,25 @@ func GetManifest(asset string) (*manifest.Manifest, error) {
}
return manifest, nil
}

// ToMap Convenience method to convert any struct into a map
func ToMap(obj interface{}) map[string]interface{} {
var result map[string]interface{}
resultRec, _ := json.Marshal(obj)
json.Unmarshal(resultRec, &result)
return result
}

// ToArg Converts a key, value pair into a valid container argument. e.g. '--argName', 'argValue' returns '--argName=argValue'
func ToArg(name, value string) string {
return name + "=" + value
}

// FromArg Converts a container argument into a key, value pair. e.g. '--argName=argValue' returns '--argName', 'argValue'
func FromArg(arg string) (key, value string) {
parts := strings.Split(arg, "=")
if len(parts) == 1 {
return parts[0], ""
}
return parts[0], parts[1]
}
Loading

0 comments on commit 66712a1

Please sign in to comment.