diff --git a/agent.go b/agent.go index bdef24e1..9e57146a 100644 --- a/agent.go +++ b/agent.go @@ -26,6 +26,7 @@ type AgentStatus struct { type AgentSpec struct { Description string `json:"description,omitempty"` SourceOf []string `json:"sourceOf" example:"Metrics,Services"` + ReleaseChannel string `json:"releaseChannel,omitempty" example:"beta,stable"` QueryDelay *QueryDelayDuration `json:"queryDelay"` AmazonPrometheus *AmazonPrometheusAgentConfig `json:"amazonPrometheus,omitempty"` AppDynamics *AppDynamicsAgentConfig `json:"appDynamics,omitempty"` diff --git a/direct.go b/direct.go index c51510d7..c7f5f81f 100644 --- a/direct.go +++ b/direct.go @@ -18,6 +18,7 @@ type DirectStatus struct { type DirectSpec struct { Description string `json:"description,omitempty" example:"Datadog description"` //nolint:lll SourceOf []string `json:"sourceOf" example:"Metrics,Services"` + ReleaseChannel string `json:"releaseChannel,omitempty" example:"beta,stable"` LogCollectionEnabled *bool `json:"logCollectionEnabled,omitempty"` HistoricalDataRetrieval *HistoricalDataRetrieval `json:"historicalDataRetrieval"` QueryDelay *QueryDelayDuration `json:"queryDelay"` diff --git a/manifest/v1alpha/objects.go b/manifest/v1alpha/objects.go index c51b4aa2..8bde279d 100644 --- a/manifest/v1alpha/objects.go +++ b/manifest/v1alpha/objects.go @@ -994,7 +994,7 @@ type QueryDelay struct { type AgentSpec struct { Description string `json:"description,omitempty" validate:"description" example:"Prometheus description"` //nolint:lll SourceOf []string `json:"sourceOf" example:"Metrics,Services"` - ReleaseChannel string `json:"releaseChannel,omitempty" example:"beta,stable"` + ReleaseChannel ReleaseChannel `json:"releaseChannel,omitempty" example:"beta,stable"` Prometheus *PrometheusAgentConfig `json:"prometheus,omitempty"` Datadog *DatadogAgentConfig `json:"datadog,omitempty"` NewRelic *NewRelicAgentConfig `json:"newRelic,omitempty"` @@ -1122,6 +1122,7 @@ type PublicDirectWithSLOs struct { type DirectSpec struct { Description string `json:"description,omitempty" validate:"description" example:"Datadog description"` //nolint:lll SourceOf []string `json:"sourceOf" example:"Metrics,Services"` + ReleaseChannel ReleaseChannel `json:"releaseChannel,omitempty" example:"beta,stable"` Datadog *DatadogDirectConfig `json:"datadog,omitempty"` LogCollectionEnabled *bool `json:"logCollectionEnabled,omitempty"` NewRelic *NewRelicDirectConfig `json:"newRelic,omitempty"` diff --git a/manifest/v1alpha/release_channel.go b/manifest/v1alpha/release_channel.go new file mode 100644 index 00000000..a08a3dd3 --- /dev/null +++ b/manifest/v1alpha/release_channel.go @@ -0,0 +1,31 @@ +package v1alpha + +import ( + "fmt" +) + +//go:generate ../../bin/go-enum --nocase --names --lower --values + +// ReleaseChannel /* ENUM(stable = 1, beta, alpha)*/ +type ReleaseChannel int + +// MarshalText implements the text marshaller method. +func (r ReleaseChannel) MarshalText() ([]byte, error) { + return []byte(r.String()), nil +} + +// UnmarshalText implements the text unmarshaller method. +func (r *ReleaseChannel) UnmarshalText(text []byte) error { + if len(text) == 0 { + *r = 0 + return nil + } + tmp, err := ParseReleaseChannel(string(text)) + if err != nil { + // We're only allowing a subset of valid release channels to be set by the user, inform only on them. + return fmt.Errorf("%s is not a valid ReleaseChannel, try [%s, %s]", + string(text), ReleaseChannelStable, ReleaseChannelBeta) + } + *r = tmp + return nil +} diff --git a/manifest/v1alpha/release_channel_enum.go b/manifest/v1alpha/release_channel_enum.go new file mode 100644 index 00000000..85de193b --- /dev/null +++ b/manifest/v1alpha/release_channel_enum.go @@ -0,0 +1,89 @@ +// Code generated by go-enum DO NOT EDIT. +// Version: 0.5.6 +// Revision: 97611fddaa414f53713597918c5e954646cb8623 +// Build Date: 2023-03-26T21:38:06Z +// Built By: goreleaser + +package v1alpha + +import ( + "fmt" + "strings" +) + +const ( + // ReleaseChannelStable is a ReleaseChannel of type Stable. + ReleaseChannelStable ReleaseChannel = iota + 1 + // ReleaseChannelBeta is a ReleaseChannel of type Beta. + ReleaseChannelBeta + // ReleaseChannelAlpha is a ReleaseChannel of type Alpha. + ReleaseChannelAlpha +) + +var ErrInvalidReleaseChannel = fmt.Errorf("not a valid ReleaseChannel, try [%s]", strings.Join(_ReleaseChannelNames, ", ")) + +const _ReleaseChannelName = "stablebetaalpha" + +var _ReleaseChannelNames = []string{ + _ReleaseChannelName[0:6], + _ReleaseChannelName[6:10], + _ReleaseChannelName[10:15], +} + +// ReleaseChannelNames returns a list of possible string values of ReleaseChannel. +func ReleaseChannelNames() []string { + tmp := make([]string, len(_ReleaseChannelNames)) + copy(tmp, _ReleaseChannelNames) + return tmp +} + +// ReleaseChannelValues returns a list of the values for ReleaseChannel +func ReleaseChannelValues() []ReleaseChannel { + return []ReleaseChannel{ + ReleaseChannelStable, + ReleaseChannelBeta, + ReleaseChannelAlpha, + } +} + +var _ReleaseChannelMap = map[ReleaseChannel]string{ + ReleaseChannelStable: _ReleaseChannelName[0:6], + ReleaseChannelBeta: _ReleaseChannelName[6:10], + ReleaseChannelAlpha: _ReleaseChannelName[10:15], +} + +// String implements the Stringer interface. +func (x ReleaseChannel) String() string { + if str, ok := _ReleaseChannelMap[x]; ok { + return str + } + return fmt.Sprintf("ReleaseChannel(%d)", x) +} + +// IsValid provides a quick way to determine if the typed value is +// part of the allowed enumerated values +func (x ReleaseChannel) IsValid() bool { + _, ok := _ReleaseChannelMap[x] + return ok +} + +var _ReleaseChannelValue = map[string]ReleaseChannel{ + _ReleaseChannelName[0:6]: ReleaseChannelStable, + strings.ToLower(_ReleaseChannelName[0:6]): ReleaseChannelStable, + _ReleaseChannelName[6:10]: ReleaseChannelBeta, + strings.ToLower(_ReleaseChannelName[6:10]): ReleaseChannelBeta, + _ReleaseChannelName[10:15]: ReleaseChannelAlpha, + strings.ToLower(_ReleaseChannelName[10:15]): ReleaseChannelAlpha, +} + +// ParseReleaseChannel attempts to convert a string to a ReleaseChannel. +func ParseReleaseChannel(name string) (ReleaseChannel, error) { + if x, ok := _ReleaseChannelValue[name]; ok { + return x, nil + } + // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. + if x, ok := _ReleaseChannelValue[strings.ToLower(name)]; ok { + return x, nil + } + return ReleaseChannel(0), fmt.Errorf("%s is %w", name, ErrInvalidReleaseChannel) +} diff --git a/manifest/v1alpha/validator.go b/manifest/v1alpha/validator.go index 36605a17..c2abae68 100644 --- a/manifest/v1alpha/validator.go +++ b/manifest/v1alpha/validator.go @@ -1506,6 +1506,10 @@ func agentSpecStructLevelValidation(sl v.StructLevel) { } agentQueryDelayValidation(sa, sl) sourceOfValidation(sa.SourceOf, sl) + + if !isValidReleaseChannel(sa.ReleaseChannel) { + sl.ReportError(sa, "ReleaseChannel", "ReleaseChannel", "unknownReleaseChannel", "") + } } func agentQueryDelayValidation(sa AgentSpec, sl v.StructLevel) { @@ -1874,6 +1878,10 @@ func directSpecStructLevelValidation(sl v.StructLevel) { directTypeValidation(sa, sl) directQueryDelayValidation(sa, sl) sourceOfValidation(sa.SourceOf, sl) + + if !isValidReleaseChannel(sa.ReleaseChannel) { + sl.ReportError(sa, "ReleaseChannel", "ReleaseChannel", "unknownReleaseChannel", "") + } } func directTypeValidation(sa DirectSpec, sl v.StructLevel) { @@ -1993,6 +2001,14 @@ func sourceOfItemsValidation(sourceOf []string, sl v.StructLevel) bool { return true } +func isValidReleaseChannel(releaseChannel ReleaseChannel) bool { + if releaseChannel == 0 { + return true + } + // We do not allow ReleaseChannelAlpha to be set by the user. + return releaseChannel.IsValid() && releaseChannel != ReleaseChannelAlpha +} + // Check performs validation, it accepts all possible structs and perform checks based on tags for structs fields func (val *Validate) Check(s interface{}) error { return val.validate.Struct(s) diff --git a/manifest/v1alpha/validator_test.go b/manifest/v1alpha/validator_test.go index 537a9b5a..c9c31bc0 100644 --- a/manifest/v1alpha/validator_test.go +++ b/manifest/v1alpha/validator_test.go @@ -1002,3 +1002,19 @@ func TestAlertConditionOpSupport(t *testing.T) { }) } } + +func TestIsReleaseChannelValid(t *testing.T) { + for name, test := range map[string]struct { + ReleaseChannel ReleaseChannel + IsValid bool + }{ + "unset release channel, valid": {IsValid: true}, + "beta channel, valid": {ReleaseChannel: ReleaseChannelBeta, IsValid: true}, + "stable channel, valid": {ReleaseChannel: ReleaseChannelStable, IsValid: true}, + "alpha channel, invalid": {ReleaseChannel: ReleaseChannelAlpha}, + } { + t.Run(name, func(t *testing.T) { + assert.Equal(t, test.IsValid, isValidReleaseChannel(test.ReleaseChannel)) + }) + } +}