Skip to content

Commit

Permalink
fixed configmap annotation issues
Browse files Browse the repository at this point in the history
Signed-off-by: warpcomdev <[email protected]>
  • Loading branch information
warpcomdev committed Mar 6, 2022
1 parent 01cab16 commit 138ced1
Show file tree
Hide file tree
Showing 17 changed files with 1,567 additions and 14 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/open-policy-agent/kube-mgmt
go 1.17

require (
github.com/hashicorp/go-multierror v1.1.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.3.0
k8s.io/api v0.23.4
Expand All @@ -18,6 +19,7 @@ require (
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/imdario/mergo v0.3.5 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
Expand All @@ -246,6 +248,8 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
Expand Down
106 changes: 92 additions & 14 deletions pkg/configmap/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import (
"context"
"encoding/json"
"fmt"
"hash/fnv"
"sort"
"strings"
"time"

"github.com/hashicorp/go-multierror"
"github.com/open-policy-agent/kube-mgmt/pkg/opa"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -186,19 +189,30 @@ func (s *Sync) add(obj interface{}) {
}

func (s *Sync) update(oldObj, obj interface{}) {
cm := obj.(*v1.ConfigMap)
oldCm, cm := oldObj.(*v1.ConfigMap), obj.(*v1.ConfigMap)
if match, isPolicy := s.matcher(cm); match {
// avoid processing new versions of the ConfigMap that don't actually
// change policy, data or labels
// (issue https://github.com/open-policy-agent/kube-mgmt/issues/131)
if cm.GetResourceVersion() != oldCm.GetResourceVersion() {
fp, oldFp := fingerprint(cm), fingerprint(oldCm)
if fp == oldFp {
return
}
}
s.syncAdd(cm, isPolicy)
} else {
// check if the label was removed
oldCm := oldObj.(*v1.ConfigMap)
if match, isPolicy := s.matcher(oldCm); match {
s.syncRemove(oldCm, isPolicy)
}
}
}

func (s *Sync) delete(obj interface{}) {
if d, ok := obj.(cache.DeletedFinalStateUnknown); ok {
obj = d.Obj
}
cm := obj.(*v1.ConfigMap)
if match, isPolicy := s.matcher(cm); match {
s.syncRemove(cm, isPolicy)
Expand All @@ -207,7 +221,15 @@ func (s *Sync) delete(obj interface{}) {

func (s *Sync) syncAdd(cm *v1.ConfigMap, isPolicy bool) {
path := fmt.Sprintf("%v/%v", cm.Namespace, cm.Name)
for key, value := range cm.Data {
// sort keys so that errors, if any, are always in the same order
sortedKeys := make([]string, 0, len(cm.Data))
for key := range cm.Data {
sortedKeys = append(sortedKeys, key)
}
sort.Strings(sortedKeys)
var multiErr *multierror.Error
for _, key := range sortedKeys {
value := cm.Data[key]
id := fmt.Sprintf("%v/%v", path, key)

var err error
Expand All @@ -225,16 +247,19 @@ func (s *Sync) syncAdd(cm *v1.ConfigMap, isPolicy bool) {
}

if err != nil {
s.setStatusAnnotation(cm, status{
Status: "error",
Error: err,
}, isPolicy)
} else {
s.setStatusAnnotation(cm, status{
Status: "ok",
}, isPolicy)
multiErr = multierror.Append(multiErr, err)
}
}
if multiErr != nil {
s.setStatusAnnotation(cm, status{
Status: "error",
Error: (*withMarshalJSON)(multiErr),
}, isPolicy)
} else {
s.setStatusAnnotation(cm, status{
Status: "ok",
}, isPolicy)
}
}

func (s *Sync) syncRemove(cm *v1.ConfigMap, isPolicy bool) {
Expand Down Expand Up @@ -265,10 +290,20 @@ func (s *Sync) setStatusAnnotation(cm *v1.ConfigMap, st status, isPolicy bool) {
if err != nil {
logrus.Errorf("Failed to serialize %v for %v/%v: %v", statusAnnotationKey, cm.Namespace, cm.Name, err)
}
annotation := string(bs)
if cm.Annotations != nil {
if existing, ok := cm.Annotations[policyStatusAnnotationKey]; ok {
if existing == annotation {
// If the annotation did not change, do not write it.
// (issue https://github.com/open-policy-agent/kube-mgmt/issues/90)
return
}
}
}
patch := map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
policyStatusAnnotationKey: string(bs),
policyStatusAnnotationKey: annotation,
},
},
}
Expand Down Expand Up @@ -298,7 +333,50 @@ func (s *Sync) syncReset(id string) {
}
}

// jsonError makes sure status struct is json serializable
type jsonError interface {
error
json.Marshaler
}

type status struct {
Status string `json:"status"`
Error error `json:"error,omitempty"`
Status string `json:"status"`
Error jsonError `json:"error,omitempty"`
}

// fingerprint for the labels and data of a configmap.
func fingerprint(cm *v1.ConfigMap) uint64 {
hash := fnv.New64a()
data := json.NewEncoder(hash)
data.Encode(cm.Labels)
data.Encode(cm.Data)
return hash.Sum64()
}

// withMarshalJSON is a json-seriazable version of multierror.Error
type withMarshalJSON multierror.Error

// MarshalJSON implements json.Marshaler
func (m *withMarshalJSON) MarshalJSON() ([]byte, error) {
if len(m.Errors) <= 0 {
return []byte(`""`), nil
}
list := make([]json.RawMessage, 0, len(m.Errors))
for _, err := range m.Errors {
if b, marshalErr := json.Marshal(err); marshalErr == nil {
list = append(list, b)
} else {
// fallback to quoted .Error() string if marshalling fails
list = append(list, []byte(fmt.Sprintf("%q", err.Error())))
}
}
if len(list) == 1 {
return list[0], nil // for backward compatibility
}
return json.Marshal(list)
}

// Error implements error
func (m *withMarshalJSON) Error() string {
return (*multierror.Error)(m).Error()
}
Loading

0 comments on commit 138ced1

Please sign in to comment.