Files
Reloader/internal/pkg/reload/matcher.go
T

282 lines
6.7 KiB
Go

package reload
import (
"regexp"
"strings"
"github.com/stakater/Reloader/internal/pkg/config"
)
// ResourceType represents the type of Kubernetes resource.
type ResourceType string
const (
// ResourceTypeConfigMap represents a ConfigMap resource.
ResourceTypeConfigMap ResourceType = "configmap"
// ResourceTypeSecret represents a Secret resource.
ResourceTypeSecret ResourceType = "secret"
)
// Kind returns the capitalized Kubernetes Kind (e.g., "ConfigMap", "Secret").
func (r ResourceType) Kind() string {
switch r {
case ResourceTypeConfigMap:
return "ConfigMap"
case ResourceTypeSecret:
return "Secret"
default:
return string(r)
}
}
// MatchResult contains the result of checking if a workload should be reloaded.
type MatchResult struct {
ShouldReload bool
AutoReload bool
Reason string
}
// Matcher determines whether a workload should be reloaded based on annotations.
type Matcher struct {
cfg *config.Config
}
// NewMatcher creates a new Matcher with the given configuration.
func NewMatcher(cfg *config.Config) *Matcher {
return &Matcher{cfg: cfg}
}
// MatchInput contains all the information needed to determine if a reload should occur.
type MatchInput struct {
ResourceName string
ResourceNamespace string
ResourceType ResourceType
ResourceAnnotations map[string]string
WorkloadAnnotations map[string]string
PodAnnotations map[string]string
}
// ShouldReload determines if a workload should be reloaded based on its annotations.
func (m *Matcher) ShouldReload(input MatchInput) MatchResult {
if m.isResourceIgnored(input.ResourceAnnotations) {
return MatchResult{
ShouldReload: false,
Reason: "resource has ignore annotation",
}
}
annotations := m.selectAnnotations(input)
if m.isResourceExcluded(input.ResourceName, input.ResourceType, annotations) {
return MatchResult{
ShouldReload: false,
Reason: "resource is in exclude list",
}
}
if m.matchesExplicitAnnotation(input.ResourceName, input.ResourceType, annotations) {
return MatchResult{
ShouldReload: true,
AutoReload: false,
Reason: "matches explicit reload annotation",
}
}
if m.matchesSearchPattern(input.ResourceAnnotations, annotations) {
return MatchResult{
ShouldReload: true,
AutoReload: true,
Reason: "matches search/match pattern",
}
}
if m.matchesAutoAnnotation(input.ResourceType, annotations) {
return MatchResult{
ShouldReload: true,
AutoReload: true,
Reason: "auto annotation enabled",
}
}
if m.matchesAutoReloadAll(input.ResourceType, annotations) {
return MatchResult{
ShouldReload: true,
AutoReload: true,
Reason: "auto-reload-all enabled",
}
}
return MatchResult{
ShouldReload: false,
Reason: "no matching annotations",
}
}
func (m *Matcher) isResourceIgnored(resourceAnnotations map[string]string) bool {
if resourceAnnotations == nil {
return false
}
return resourceAnnotations[m.cfg.Annotations.Ignore] == "true"
}
func (m *Matcher) selectAnnotations(input MatchInput) map[string]string {
if m.hasRelevantAnnotations(input.WorkloadAnnotations, input.ResourceType) {
return input.WorkloadAnnotations
}
if m.hasRelevantAnnotations(input.PodAnnotations, input.ResourceType) {
return input.PodAnnotations
}
return input.WorkloadAnnotations
}
func (m *Matcher) hasRelevantAnnotations(annotations map[string]string, resourceType ResourceType) bool {
if annotations == nil {
return false
}
explicitAnn := m.getExplicitAnnotation(resourceType)
if _, ok := annotations[explicitAnn]; ok {
return true
}
if _, ok := annotations[m.cfg.Annotations.Search]; ok {
return true
}
if _, ok := annotations[m.cfg.Annotations.Auto]; ok {
return true
}
typedAutoAnn := m.getTypedAutoAnnotation(resourceType)
if _, ok := annotations[typedAutoAnn]; ok {
return true
}
return false
}
func (m *Matcher) isResourceExcluded(resourceName string, resourceType ResourceType, annotations map[string]string) bool {
if annotations == nil {
return false
}
var excludeAnn string
switch resourceType {
case ResourceTypeConfigMap:
excludeAnn = m.cfg.Annotations.ConfigmapExclude
case ResourceTypeSecret:
excludeAnn = m.cfg.Annotations.SecretExclude
}
excludeList, ok := annotations[excludeAnn]
if !ok || excludeList == "" {
return false
}
for _, excluded := range strings.Split(excludeList, ",") {
if strings.TrimSpace(excluded) == resourceName {
return true
}
}
return false
}
func (m *Matcher) matchesExplicitAnnotation(resourceName string, resourceType ResourceType, annotations map[string]string) bool {
if annotations == nil {
return false
}
explicitAnn := m.getExplicitAnnotation(resourceType)
annotationValue, ok := annotations[explicitAnn]
if !ok || annotationValue == "" {
return false
}
for _, value := range strings.Split(annotationValue, ",") {
value = strings.TrimSpace(value)
if value == "" {
continue
}
re, err := regexp.Compile("^" + value + "$")
if err != nil {
if value == resourceName {
return true
}
continue
}
if re.MatchString(resourceName) {
return true
}
}
return false
}
func (m *Matcher) matchesSearchPattern(resourceAnnotations, workloadAnnotations map[string]string) bool {
if workloadAnnotations == nil || resourceAnnotations == nil {
return false
}
searchValue, ok := workloadAnnotations[m.cfg.Annotations.Search]
if !ok || searchValue != "true" {
return false
}
matchValue, ok := resourceAnnotations[m.cfg.Annotations.Match]
return ok && matchValue == "true"
}
func (m *Matcher) matchesAutoAnnotation(resourceType ResourceType, annotations map[string]string) bool {
if annotations == nil {
return false
}
if annotations[m.cfg.Annotations.Auto] == "true" {
return true
}
typedAutoAnn := m.getTypedAutoAnnotation(resourceType)
return annotations[typedAutoAnn] == "true"
}
func (m *Matcher) matchesAutoReloadAll(resourceType ResourceType, annotations map[string]string) bool {
if !m.cfg.AutoReloadAll {
return false
}
if annotations != nil {
if annotations[m.cfg.Annotations.Auto] == "false" {
return false
}
typedAutoAnn := m.getTypedAutoAnnotation(resourceType)
if annotations[typedAutoAnn] == "false" {
return false
}
}
return true
}
func (m *Matcher) getExplicitAnnotation(resourceType ResourceType) string {
switch resourceType {
case ResourceTypeConfigMap:
return m.cfg.Annotations.ConfigmapReload
case ResourceTypeSecret:
return m.cfg.Annotations.SecretReload
default:
return ""
}
}
func (m *Matcher) getTypedAutoAnnotation(resourceType ResourceType) string {
switch resourceType {
case ResourceTypeConfigMap:
return m.cfg.Annotations.ConfigmapAuto
case ResourceTypeSecret:
return m.cfg.Annotations.SecretAuto
default:
return ""
}
}