-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TEP-0137] Add events config map #6883
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Copyright 2023 The Tekton Authors | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
apiVersion: v1 | ||
kind: ConfigMap | ||
metadata: | ||
name: config-events | ||
namespace: tekton-pipelines | ||
labels: | ||
app.kubernetes.io/instance: default | ||
app.kubernetes.io/part-of: tekton-pipelines | ||
data: | ||
_example: | | ||
################################ | ||
# # | ||
# EXAMPLE CONFIGURATION # | ||
# # | ||
################################ | ||
|
||
# This block is not actually functional configuration, | ||
# but serves to illustrate the available configuration | ||
# options and document them in a way that is accessible | ||
# to users that `kubectl edit` this config map. | ||
# | ||
# These sample configuration options may be copied out of | ||
# this example block and unindented to be in the data block | ||
# to actually change the configuration. | ||
|
||
# formats contains a comma seperated list of event formats to be used | ||
# the only format supported today is "tektonv1". An empty string is not | ||
# a valid configuration. To disable events, do not specify the sink. | ||
formats: "tektonv1" | ||
|
||
# sink contains the event sink to be used for TaskRun, PipelineRun and | ||
# CustomRun. If no sink is specified, no CloudEvent is generated. | ||
# This setting supercedes the "default-cloud-events-sink" from the | ||
# "config-defaults" config map | ||
sink: "https://events.sink/cdevents" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/* | ||
Copyright 2023 The Tekton Authors | ||
|
||
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 config | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"sort" | ||
"strings" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
const ( | ||
// FormatTektonV1 represents the "v1" events in Tekton custom format | ||
FormatTektonV1 EventFormat = "tektonv1" | ||
|
||
// DefaultSink is the default value for "sink" | ||
DefaultSink = "" | ||
|
||
formatsKey = "formats" | ||
sinkKey = "sink" | ||
) | ||
|
||
var ( | ||
// TODO(afrittoli): only one valid format for now, more to come | ||
// See TEP-0137 https://github.com/tektoncd/community/pull/1028 | ||
validFormats = EventFormats{FormatTektonV1: struct{}{}} | ||
|
||
// DefaultFormat is the default value for "formats" | ||
DefaultFormats = EventFormats{FormatTektonV1: struct{}{}} | ||
|
||
// DefaultConfig holds all the default configurations for the config. | ||
DefaultEvents, _ = NewEventsFromMap(map[string]string{}) | ||
) | ||
|
||
// Events holds the events configurations | ||
// +k8s:deepcopy-gen=true | ||
type Events struct { | ||
Sink string | ||
Formats EventFormats | ||
} | ||
|
||
// EventFormat is a single event format | ||
type EventFormat string | ||
|
||
// EventFormats is a set of event formats | ||
type EventFormats map[EventFormat]struct{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: could https://pkg.go.dev/k8s.io/[email protected]/pkg/util/sets#Set be used here? It looks like the main thing missing is just a string representation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I'm pretty sure we could use that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried a bit, but go stuck trying to wrap my own type around the |
||
|
||
// String is a string representation of an EventFormat | ||
func (ef EventFormat) String() string { | ||
return string(ef) | ||
} | ||
|
||
// IsValid returns true is the EventFormat one of the valid ones | ||
func (ef EventFormat) IsValid() bool { | ||
_, ok := validFormats[ef] | ||
return ok | ||
} | ||
|
||
// String is a string representation of an EventFormats | ||
func (efs EventFormats) String() string { | ||
// Make an array of map keys | ||
keys := make([]string, len(efs)) | ||
|
||
i := 0 | ||
for k := range efs { | ||
keys[i] = k.String() | ||
i++ | ||
} | ||
// Sorting helps with testing | ||
sort.Strings(keys) | ||
|
||
// Build a comma separated list | ||
return strings.Join(keys, ",") | ||
} | ||
|
||
// Equals defines identity between EventFormats | ||
func (efs EventFormats) Equals(other EventFormats) bool { | ||
if len(efs) != len(other) { | ||
return false | ||
} | ||
for key := range efs { | ||
if _, ok := other[key]; !ok { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// ParseEventFormats converts a comma separated list into a EventFormats set | ||
func ParseEventFormats(formats string) (EventFormats, error) { | ||
// An empty string is not a valid configuration | ||
if formats == "" { | ||
return EventFormats{}, fmt.Errorf("formats cannot be empty") | ||
} | ||
stringFormats := strings.Split(formats, ",") | ||
var eventFormats EventFormats = make(map[EventFormat]struct{}, len(stringFormats)) | ||
for _, format := range stringFormats { | ||
if !EventFormat(format).IsValid() { | ||
return EventFormats{}, fmt.Errorf("invalid format: %s", format) | ||
} | ||
// If already in the map (duplicate), fail | ||
if _, ok := eventFormats[EventFormat(format)]; ok { | ||
return EventFormats{}, fmt.Errorf("duplicate format: %s", format) | ||
} | ||
eventFormats[EventFormat(format)] = struct{}{} | ||
} | ||
return eventFormats, nil | ||
} | ||
|
||
// GetEventsConfigName returns the name of the configmap containing all | ||
// feature flags. | ||
func GetEventsConfigName() string { | ||
if e := os.Getenv("CONFIG_EVENTS_NAME"); e != "" { | ||
return e | ||
} | ||
return "config-events" | ||
} | ||
|
||
// NewEventsFromMap returns a Config given a map corresponding to a ConfigMap | ||
func NewEventsFromMap(cfgMap map[string]string) (*Events, error) { | ||
// for any string field with no extra validation | ||
setField := func(key string, defaultValue string, field *string) { | ||
if cfg, ok := cfgMap[key]; ok { | ||
*field = cfg | ||
} else { | ||
*field = defaultValue | ||
} | ||
} | ||
|
||
events := Events{} | ||
err := setFormats(cfgMap, DefaultFormats, &events.Formats) | ||
if err != nil { | ||
return nil, err | ||
} | ||
setField(sinkKey, DefaultSink, &events.Sink) | ||
return &events, nil | ||
} | ||
|
||
func setFormats(cfgMap map[string]string, defaultValue EventFormats, field *EventFormats) error { | ||
value := defaultValue | ||
if cfg, ok := cfgMap[formatsKey]; ok { | ||
v, err := ParseEventFormats(cfg) | ||
if err != nil { | ||
return err | ||
} | ||
value = v | ||
} | ||
*field = value | ||
return nil | ||
} | ||
|
||
// NewEventsFromConfigMap returns a Config for the given configmap | ||
func NewEventsFromConfigMap(config *corev1.ConfigMap) (*Events, error) { | ||
return NewEventsFromMap(config.Data) | ||
} | ||
|
||
// Equals returns true if two Configs are identical | ||
func (cfg *Events) Equals(other *Events) bool { | ||
if cfg == nil && other == nil { | ||
return true | ||
} | ||
|
||
if cfg == nil || other == nil { | ||
return false | ||
} | ||
|
||
return other.Sink == cfg.Sink && | ||
other.Formats.Equals(cfg.Formats) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add this to the deprecations table?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely will. I was a bit put off by the fact that I need to put things like date and release number in there, but I can start with a basic entry and update it once things are merged and released
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - deprecating a config parameter in favour of a new one that provides the same functionality is not really covered (or in the scope of) the API compatibility policy. Since the config option has been around for a while and there's no real extra maintenance burden in keeping it around, I marked it for removal in 9 months