Skip to content

Commit

Permalink
Make ConfigMap name configurable (#30)
Browse files Browse the repository at this point in the history
* add ConfigMapName to CRD
* require built binary for docker build
* add "cookie_session" to list of defaultAuthenticatorsAvailable
* handle deletion
  • Loading branch information
paulbdavis authored and aeneasr committed Nov 18, 2019
1 parent 9e31717 commit e9cf7a1
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 15 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ generate: controller-gen
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./api/...

# Build the docker image
docker-build-notest:
docker-build-notest: manager
docker build . -t ${IMG}
@echo "updating kustomize image patch file for manager resource"
sed -i'' -e 's@image: .*@image: '"${IMG}"'@' ./config/default/manager_image_patch.yaml
Expand All @@ -91,3 +91,4 @@ CONTROLLER_GEN=$(shell which controller-gen)
else
CONTROLLER_GEN=$(shell which controller-gen)
endif

38 changes: 37 additions & 1 deletion api/v1alpha1/rule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type RuleSpec struct {
Authenticators []*Authenticator `json:"authenticators,omitempty"`
Authorizer *Authorizer `json:"authorizer,omitempty"`
Mutators []*Mutator `json:"mutators,omitempty"`
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
//
// ConfigMapName points to the K8s ConfigMap that contains these rules
ConfigMapName *string `json:"configMapName,omitempty"`
}

// Validation defines the validation state of Rule
Expand Down Expand Up @@ -129,7 +135,7 @@ func (rl RuleList) ToOathkeeperRules() ([]byte, error) {

rules := make([]*RuleJSON, len(rl.Items))

for i, _ := range rl.Items {
for i := range rl.Items {
rules[i] = rl.Items[i].ToRuleJSON()
}

Expand All @@ -149,6 +155,36 @@ func (rl RuleList) FilterNotValid() RuleList {
return rlCopy
}

// FilterConfigMapName filters out Rules that don't effect the given ConfigMap
func (rl RuleList) FilterConfigMapName(name *string) RuleList {
rlCopy := rl
validRules := []Rule{}
for _, rule := range rl.Items {
if rule.Spec.ConfigMapName == nil {
if name == nil {
validRules = append(validRules, rule)
}
} else if *rule.Spec.ConfigMapName == *name {
validRules = append(validRules, rule)
}
}
rlCopy.Items = validRules
return rlCopy
}

// FilterOutRule filters out the provided rule from the rule list, for re-generating the rules when a rule is deleted
func (rl RuleList) FilterOutRule(r Rule) RuleList {
rlCopy := rl
validRules := []Rule{}
for _, rule := range rl.Items {
if rule.ObjectMeta.SelfLink != r.ObjectMeta.SelfLink {
validRules = append(validRules, rule)
}
}
rlCopy.Items = validRules
return rlCopy
}

// ValidateWith uses provided validation configuration to check whether the rule have proper handlers set. Nil is a valid handler.
func (r Rule) ValidateWith(config validation.Config) error {

Expand Down
9 changes: 7 additions & 2 deletions api/v1alpha1/rule_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ func TestToOathkeeperRules(t *testing.T) {
"http://my-backend-service1",
"http://my-app/some-route1",
newStringPtr("/api/v1"),
nil,
newBoolPtr(true),
[]*Authenticator{&Authenticator{h1}},
nil,
Expand All @@ -200,6 +201,7 @@ func TestToOathkeeperRules(t *testing.T) {
"http://my-backend-service2",
"http://my-app/some-route2",
nil,
nil,
newBoolPtr(false),
[]*Authenticator{&Authenticator{h1}, {h2}},
nil,
Expand All @@ -213,6 +215,7 @@ func TestToOathkeeperRules(t *testing.T) {
nil,
nil,
nil,
nil,
&Authorizer{h1},
nil)

Expand Down Expand Up @@ -313,6 +316,7 @@ func TestValidateWith(t *testing.T) {
"http://my-backend-service1",
"http://my-app/some-route1",
newStringPtr("/api/v1"),
nil,
newBoolPtr(true),
nil,
nil,
Expand Down Expand Up @@ -390,10 +394,10 @@ func TestFilterNotValid(t *testing.T) {
}

func newStaticRule(authenticators []*Authenticator, authorizer *Authorizer, mutators []*Mutator) *Rule {
return newRule("r1", "test", "", "", newStringPtr(""), newBoolPtr(false), authenticators, authorizer, mutators)
return newRule("r1", "test", "", "", newStringPtr(""), nil, newBoolPtr(false), authenticators, authorizer, mutators)
}

func newRule(name, namespace, upstreamURL, matchURL string, stripURLPath *string, preserveURLHost *bool, authenticators []*Authenticator, authorizer *Authorizer, mutators []*Mutator) *Rule {
func newRule(name, namespace, upstreamURL, matchURL string, stripURLPath, configMapName *string, preserveURLHost *bool, authenticators []*Authenticator, authorizer *Authorizer, mutators []*Mutator) *Rule {

spec := RuleSpec{
Upstream: &Upstream{
Expand All @@ -408,6 +412,7 @@ func newRule(name, namespace, upstreamURL, matchURL string, stripURLPath *string
Authenticators: authenticators,
Authorizer: authorizer,
Mutators: mutators,
ConfigMapName: configMapName,
}

return &Rule{
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ func (in *RuleSpec) DeepCopyInto(out *RuleSpec) {
}
}
}
if in.ConfigMapName != nil {
in, out := &in.ConfigMapName, &out.ConfigMapName
*out = new(string)
**out = **in
}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleSpec.
Expand Down
7 changes: 7 additions & 0 deletions config/crd/bases/oathkeeper.ory.sh_rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,13 @@ spec:
required:
- handler
type: object
configMapName:
description: ConfigMapName points to the K8s ConfigMap that contains
these rules
maxLength: 253
minLength: 1
pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*'
type: string
match:
properties:
methods:
Expand Down
75 changes: 65 additions & 10 deletions controllers/rule_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
const (
retryAttempts = 5
retryDelay = time.Second * 2
FinalizerName = "finalizer.ory.oathkeeper.sh"
)

// RuleReconciler reconciles a Rule object
Expand All @@ -64,10 +65,10 @@ func (r *RuleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {

if err := r.Get(ctx, req.NamespacedName, &rule); err != nil {
if apierrs.IsNotFound(err) {
skipValidation = true
} else {
return ctrl.Result{}, err
// just return here, the finalizers have already run
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}

if !skipValidation {
Expand Down Expand Up @@ -96,16 +97,50 @@ func (r *RuleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {

var rulesList oathkeeperv1alpha1.RuleList

if err := r.List(ctx, &rulesList); err != nil {
if err := r.List(ctx, &rulesList, client.InNamespace(req.NamespacedName.Namespace)); err != nil {
return ctrl.Result{}, err
}

oathkeeperRulesJSON, err := rulesList.FilterNotValid().ToOathkeeperRules()
// examine DeletionTimestamp to determine if object is under deletion
if rule.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !containsString(rule.ObjectMeta.Finalizers, FinalizerName) {
rule.ObjectMeta.Finalizers = append(rule.ObjectMeta.Finalizers, FinalizerName)
if err := r.Update(ctx, &rule); err != nil {
return ctrl.Result{}, err
}
}
} else {
// The object is being deleted
if containsString(rule.ObjectMeta.Finalizers, FinalizerName) {
// our finalizer is present, so lets handle any external dependency
rulesList = rulesList.FilterOutRule(rule)

// remove our finalizer from the list and update it.
rule.ObjectMeta.Finalizers = removeString(rule.ObjectMeta.Finalizers, FinalizerName)
if err := r.Update(ctx, &rule); err != nil {
return ctrl.Result{}, err
}
}
}

oathkeeperRulesJSON, err := rulesList.FilterNotValid().
FilterConfigMapName(rule.Spec.ConfigMapName).
ToOathkeeperRules()
if err != nil {
return ctrl.Result{}, err
}

if err := r.updateOrCreateRulesConfigmap(ctx, string(oathkeeperRulesJSON)); err != nil {
configMap := r.RuleConfigmap
if rule.Spec.ConfigMapName != nil {
configMap = types.NamespacedName{
Name: *rule.Spec.ConfigMapName,
Namespace: req.NamespacedName.Namespace,
}
}
if err := r.updateOrCreateRulesConfigmap(ctx, configMap, string(oathkeeperRulesJSON)); err != nil {
r.Log.Error(err, "unable to process rules Configmap")
os.Exit(1)
}
Expand All @@ -121,14 +156,14 @@ func (r *RuleReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func (r *RuleReconciler) updateOrCreateRulesConfigmap(ctx context.Context, data string) error {
func (r *RuleReconciler) updateOrCreateRulesConfigmap(ctx context.Context, configMap types.NamespacedName, data string) error {

var oathkeeperRulesConfigmap apiv1.ConfigMap
var exists = false

fetchMapFunc := func() error {

if err := r.Get(ctx, r.RuleConfigmap, &oathkeeperRulesConfigmap); err != nil {
if err := r.Get(ctx, configMap, &oathkeeperRulesConfigmap); err != nil {

if apierrs.IsForbidden(err) {
return retry.Unrecoverable(err)
Expand All @@ -149,8 +184,8 @@ func (r *RuleReconciler) updateOrCreateRulesConfigmap(ctx context.Context, data
r.Log.Info("creating ConfigMap")
oathkeeperRulesConfigmap = apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: r.RuleConfigmap.Name,
Namespace: r.RuleConfigmap.Namespace,
Name: configMap.Name,
Namespace: configMap.Namespace,
},
Data: map[string]string{r.RulesFileName: data},
}
Expand Down Expand Up @@ -207,3 +242,23 @@ func boolPtr(b bool) *bool {
func stringPtr(s string) *string {
return &s
}

// Helper functions to check and remove string from a slice of strings.
func containsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}

func removeString(slice []string, s string) (result []string) {
for _, item := range slice {
if item == s {
continue
}
result = append(result, item)
}
return
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
defaultAuthenticatorsAvailable = [...]string{"noop", "unauthorized", "anonymous", "oauth2_client_credentials", "oauth2_introspection", "jwt"}
defaultAuthenticatorsAvailable = [...]string{"noop", "unauthorized", "anonymous", "cookie_session", "oauth2_client_credentials", "oauth2_introspection", "jwt"}
defaultAuthorizersAvailable = [...]string{"allow", "deny", "keto_engine_acp_ory"}
defaultMutatorsAvailable = [...]string{"noop", "id_token", "header", "cookie", "hydrator"}
)
Expand Down

0 comments on commit e9cf7a1

Please sign in to comment.