mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 05:23:04 +00:00
5fb9df1640
* Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
239 lines
6.5 KiB
Go
239 lines
6.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package structs
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/api"
|
|
)
|
|
|
|
// ResourceReference is a reference to a ConfigEntry
|
|
// with an optional reference to a subsection of that ConfigEntry
|
|
// that can be specified as SectionName
|
|
type ResourceReference struct {
|
|
// Kind is the kind of ConfigEntry that this resource refers to.
|
|
Kind string
|
|
// Name is the identifier for the ConfigEntry this resource refers to.
|
|
Name string
|
|
// SectionName is a generic subresource identifier that specifies
|
|
// a subset of the ConfigEntry to which this reference applies. Usage
|
|
// of this field should be up to the controller that leverages it. If
|
|
// unused, this should be blank.
|
|
SectionName string
|
|
|
|
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
|
}
|
|
|
|
func (r *ResourceReference) String() string {
|
|
return fmt.Sprintf("%s:%s/%s/%s/%s", r.Kind, r.PartitionOrDefault(), r.NamespaceOrDefault(), r.Name, r.SectionName)
|
|
}
|
|
|
|
func (r *ResourceReference) IsSame(other *ResourceReference) bool {
|
|
if r == nil && other == nil {
|
|
return true
|
|
}
|
|
if r == nil || other == nil {
|
|
return false
|
|
}
|
|
return r.Kind == other.Kind &&
|
|
r.Name == other.Name &&
|
|
r.SectionName == other.SectionName &&
|
|
r.EnterpriseMeta.IsSame(&other.EnterpriseMeta)
|
|
}
|
|
|
|
// Status is used for propagating back asynchronously calculated
|
|
// messages from control loops to a user
|
|
type Status struct {
|
|
// Conditions is the set of condition objects associated with
|
|
// a ConfigEntry status.
|
|
Conditions []Condition
|
|
}
|
|
|
|
func (s *Status) MatchesConditionStatus(condition Condition) bool {
|
|
for _, c := range s.Conditions {
|
|
if c.IsCondition(&condition) &&
|
|
c.Status == condition.Status {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s Status) SameConditions(other Status) bool {
|
|
if len(s.Conditions) != len(other.Conditions) {
|
|
return false
|
|
}
|
|
sortConditions := func(conditions []Condition) []Condition {
|
|
sort.SliceStable(conditions, func(i, j int) bool {
|
|
if conditions[i].Type < conditions[j].Type {
|
|
return true
|
|
}
|
|
if conditions[i].Type > conditions[j].Type {
|
|
return false
|
|
}
|
|
return lessResource(conditions[i].Resource, conditions[j].Resource)
|
|
})
|
|
return conditions
|
|
}
|
|
oneConditions := sortConditions(s.Conditions)
|
|
twoConditions := sortConditions(other.Conditions)
|
|
for i, condition := range oneConditions {
|
|
other := twoConditions[i]
|
|
if !condition.IsSame(&other) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func lessResource(one, two *ResourceReference) bool {
|
|
if one == nil && two == nil {
|
|
return false
|
|
}
|
|
if one == nil {
|
|
return true
|
|
}
|
|
if two == nil {
|
|
return false
|
|
}
|
|
if one.Kind < two.Kind {
|
|
return true
|
|
}
|
|
if one.Kind > two.Kind {
|
|
return false
|
|
}
|
|
if one.Name < two.Name {
|
|
return true
|
|
}
|
|
if one.Name > two.Name {
|
|
return false
|
|
}
|
|
return one.SectionName < two.SectionName
|
|
}
|
|
|
|
// Condition is used for a single message and state associated
|
|
// with an object. For example, a ConfigEntry that references
|
|
// multiple other resources may have different statuses with
|
|
// respect to each of those resources.
|
|
type Condition struct {
|
|
// Type is a value from a bounded set of types that an object might have
|
|
Type string
|
|
// Status is a value from a bounded set of statuses that an object might have
|
|
Status string
|
|
// Reason is a value from a bounded set of reasons for a given status
|
|
Reason string
|
|
// Message is a message that gives more detailed information about
|
|
// why a Condition has a given status and reason
|
|
Message string
|
|
// Resource is an optional reference to a resource for which this
|
|
// condition applies
|
|
Resource *ResourceReference
|
|
// LastTransitionTime is the time at which this Condition was created
|
|
LastTransitionTime *time.Time
|
|
}
|
|
|
|
func (c *Condition) IsCondition(other *Condition) bool {
|
|
return c.Type == other.Type && c.Resource.IsSame(other.Resource)
|
|
}
|
|
|
|
func (c *Condition) IsSame(other *Condition) bool {
|
|
return c.IsCondition(other) &&
|
|
c.Status == other.Status &&
|
|
c.Reason == other.Reason &&
|
|
c.Message == other.Message
|
|
}
|
|
|
|
type StatusUpdater struct {
|
|
entry ControlledConfigEntry
|
|
status Status
|
|
}
|
|
|
|
func NewStatusUpdater(entry ControlledConfigEntry) *StatusUpdater {
|
|
status := entry.GetStatus()
|
|
return &StatusUpdater{
|
|
entry: entry,
|
|
status: *status.DeepCopy(),
|
|
}
|
|
}
|
|
|
|
func (u *StatusUpdater) SetCondition(condition Condition) {
|
|
for i, c := range u.status.Conditions {
|
|
if c.IsCondition(&condition) {
|
|
if !c.IsSame(&condition) {
|
|
// the conditions aren't identical, merge this one in
|
|
u.status.Conditions[i] = condition
|
|
}
|
|
// we either set the condition or it was already set, so
|
|
// just return
|
|
return
|
|
}
|
|
}
|
|
u.status.Conditions = append(u.status.Conditions, condition)
|
|
}
|
|
|
|
func (u *StatusUpdater) ClearConditions() {
|
|
u.status.Conditions = []Condition{}
|
|
}
|
|
|
|
func (u *StatusUpdater) RemoveCondition(condition Condition) {
|
|
filtered := []Condition{}
|
|
for _, c := range u.status.Conditions {
|
|
if !c.IsCondition(&condition) {
|
|
filtered = append(filtered, c)
|
|
}
|
|
}
|
|
u.status.Conditions = filtered
|
|
}
|
|
|
|
func (u *StatusUpdater) UpdateEntry() (ControlledConfigEntry, bool) {
|
|
if u.status.SameConditions(u.entry.GetStatus()) {
|
|
return nil, false
|
|
}
|
|
u.entry.SetStatus(u.status)
|
|
return u.entry, true
|
|
}
|
|
|
|
func NewGatewayCondition(name api.GatewayConditionType, status api.ConditionStatus, reason api.GatewayConditionReason, message string, resource ResourceReference) Condition {
|
|
if err := api.ValidateGatewayConditionReason(name, status, reason); err != nil {
|
|
// note we panic here because an invalid combination is a programmer error
|
|
// this should never actually be hit
|
|
panic(err)
|
|
}
|
|
|
|
return Condition{
|
|
Type: string(name),
|
|
Status: string(status),
|
|
Reason: string(reason),
|
|
Message: message,
|
|
Resource: ptrTo(resource),
|
|
LastTransitionTime: ptrTo(time.Now().UTC()),
|
|
}
|
|
}
|
|
|
|
// NewRouteCondition is a helper to build allowable Conditions for a Route config entry
|
|
func NewRouteCondition(name api.RouteConditionType, status api.ConditionStatus, reason api.RouteConditionReason, message string, ref ResourceReference) Condition {
|
|
if err := api.ValidateRouteConditionReason(name, status, reason); err != nil {
|
|
// note we panic here because an invalid combination is a programmer error
|
|
// this should never actually be hit
|
|
panic(err)
|
|
}
|
|
|
|
return Condition{
|
|
Type: string(name),
|
|
Status: string(status),
|
|
Reason: string(reason),
|
|
Message: message,
|
|
Resource: ptrTo(ref),
|
|
LastTransitionTime: ptrTo(time.Now().UTC()),
|
|
}
|
|
}
|
|
|
|
func ptrTo[T any](val T) *T {
|
|
return &val
|
|
}
|