Skip to content

Commit

Permalink
rewrite examples
Browse files Browse the repository at this point in the history
  • Loading branch information
nieomylnieja committed Jul 1, 2024
1 parent e4adf41 commit 0b7dbc3
Show file tree
Hide file tree
Showing 16 changed files with 313 additions and 177 deletions.
163 changes: 80 additions & 83 deletions internal/cmd/examplegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,20 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"

"github.com/goccy/go-yaml"

v1alphaExamples "github.com/nobl9/nobl9-go/internal/manifest/v1alpha/examples"
"github.com/nobl9/nobl9-go/internal/pathutils"
"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
v1alphaSLO "github.com/nobl9/nobl9-go/manifest/v1alpha/slo"
"github.com/nobl9/nobl9-go/sdk"
)

type examplesGeneratorFunc func() any

type examplesGeneratorConfig struct {
Generate examplesGeneratorFunc
Examples []v1alphaExamples.Example
Path string
Comments yaml.CommentMap
}
Expand All @@ -31,20 +28,82 @@ func main() {
rootPath := pathutils.FindModuleRoot()
configs := getV1alphaExamplesConfigs()
for _, config := range configs {
v := config.Generate()
if object, ok := v.(manifest.Object); ok && config.Path == "" {
config.Path = filepath.Join(
manifestPath,
object.GetVersion().VersionString(),
object.GetKind().ToLower(),
"examples.yaml",
)
for _, variant := range config.Examples {
v := variant.GetObject()
if object, ok := v.(manifest.Object); ok && config.Path == "" {
config.Path = filepath.Join(
manifestPath,
object.GetVersion().VersionString(),
object.GetKind().ToLower(),
"examples.yaml",
)
}
config.Path = filepath.Join(rootPath, config.Path)
if err := writeExamples(v, config.Path, config.Comments); err != nil {
errFatal(err.Error())
}
}
}
}

func getV1alphaExamplesConfigs() []examplesGeneratorConfig {
basePath := filepath.Join(manifestPath, "v1alpha")
// Non-standard examples.
configs := []examplesGeneratorConfig{
{
Examples: v1alphaExamples.Labels(),
Path: filepath.Join(basePath, "labels_examples.yaml"),
},
{
Examples: v1alphaExamples.MetadataAnnotations(),
Path: filepath.Join(basePath, "metadata_annotations_examples.yaml"),
},
}
// Standard examples.
allExamples := [][]v1alphaExamples.Example{
v1alphaExamples.Project(),
v1alphaExamples.Service(),
v1alphaExamples.AlertMethod(),
v1alphaExamples.SLO(),
}
for _, examples := range allExamples {
object := examples[0].(manifest.Object)
basePath := filepath.Join(
manifestPath,
object.GetVersion().VersionString(),
object.GetKind().ToLower(),
)
grouped := groupBy(examples, func(e v1alphaExamples.Example) string { return e.GetVariant() })
// If we don't have any variants, we can write all examples into examples.yaml file.
if len(grouped) == 1 {
configs = append(configs, examplesGeneratorConfig{
Examples: examples,
Path: filepath.Join(basePath, "examples.yaml"),
})
continue
}
config.Path = filepath.Join(rootPath, config.Path)
if err := writeExamples(v, config.Path, config.Comments); err != nil {
errFatal(err.Error())
for variant, examples := range grouped {
config := examplesGeneratorConfig{
Examples: examples,
Path: filepath.Join(basePath, "examples", strings.ToLower(variant)+".yaml"),
Comments: make(yaml.CommentMap),
}
if len(examples) == 1 {
configs = append(configs, config)
continue
}
for i, example := range examples {
comments := example.GetYAMLComments()
if len(comments) == 0 {
continue
}
config.Comments[fmt.Sprintf("$[%d]", i)] = []*yaml.Comment{yaml.HeadComment(comments...)}
}
configs = append(configs, config)
}
}
// config = append(config, getV1alphaSLOExamplesConfigs(path)...)
return configs
}

func writeExamples(v any, path string, comments yaml.CommentMap) error {
Expand All @@ -67,78 +126,16 @@ func writeExamples(v any, path string, comments yaml.CommentMap) error {
return enc.Encode(v)
}

func getV1alphaExamplesConfigs() []examplesGeneratorConfig {
path := filepath.Join(manifestPath, "v1alpha")
config := []examplesGeneratorConfig{
{Generate: generify(v1alphaExamples.Project)},
{Generate: generify(v1alphaExamples.Service)},
{
Generate: generify(v1alphaExamples.Labels),
Path: filepath.Join(path, "labels_examples.yaml"),
},
{
Generate: generify(v1alphaExamples.MetadataAnnotations),
Path: filepath.Join(path, "metadata_annotations_examples.yaml"),
},
}
config = append(config, getV1alphaSLOExamplesConfigs(path)...)
return config
}

func getV1alphaSLOExamplesConfigs(path string) []examplesGeneratorConfig {
variantsPerDataSource := make(
map[v1alpha.DataSourceType][]v1alphaExamples.SLOVariant,
len(v1alpha.DataSourceTypeValues()),
)
for _, variant := range v1alphaExamples.SLO() {
variantsPerDataSource[variant.DataSourceType] = append(
variantsPerDataSource[variant.DataSourceType],
variant,
)
}
config := make([]examplesGeneratorConfig, 0, len(variantsPerDataSource))
for dataSourceType, variants := range variantsPerDataSource {
comments := make(yaml.CommentMap, len(variants))
for i, variant := range variants {
texts := []string{
fmt.Sprintf(" Metric type: %s", variant.MetricVariant),
fmt.Sprintf(" Budgeting method: %s", variant.BudgetingMethod),
fmt.Sprintf(" Time window type: %s", variant.TimeWindowType),
}
if variant.MetricSubVariant != "" {
texts = slices.Insert(texts, 1, fmt.Sprintf(" Metric variant: %s", variant.MetricSubVariant))
}
comments[fmt.Sprintf("$[%d]", i)] = []*yaml.Comment{yaml.HeadComment(texts...)}
}
config = append(config, examplesGeneratorConfig{
Generate: func() any {
return mapSlice(variants, func(v v1alphaExamples.SLOVariant) v1alphaSLO.SLO { return v.SLO })
},
Path: filepath.Join(
path,
"slo",
"examples",
fmt.Sprintf("%s.yaml", strings.ToLower(dataSourceType.String())),
),
Comments: comments,
})
}
return config
}

func generify[T any](generator func() T) examplesGeneratorFunc {
return func() any { return generator() }
}

func errFatal(f string) {
fmt.Fprintln(os.Stderr, f)
os.Exit(1)
}

func mapSlice[T, N any](s []T, m func(T) N) []N {
r := make([]N, len(s))
for i, v := range s {
r[i] = m(v)
func groupBy[K comparable, V any](s []V, key func(V) K) map[K][]V {
m := make(map[K][]V)
for _, v := range s {
k := key(v)
m[k] = append(m[k], v)
}
return r
return m
}
85 changes: 53 additions & 32 deletions internal/manifest/v1alpha/examples/alert_method.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,66 +9,87 @@ import (
"github.com/nobl9/nobl9-go/sdk"
)

type alertMethodVariant = string
type alertMethodSpecSubVariant = string

const (
alertMethodVariantWebhookTemplate metricVariant = "template"
alertMethodVariantWebhookTemplateFields metricVariant = "template fields"
alertMethodSpecSubVariantWebhookTemplate metricVariant = "template"
alertMethodSpecSubVariantWebhookTemplateFields metricVariant = "templateFields"
)

var standardAlertMethods = []v1alpha.AlertMethodType{}
var standardAlertMethods = []v1alpha.AlertMethodType{
v1alpha.AlertMethodTypePagerDuty,
v1alpha.AlertMethodTypeSlack,
v1alpha.AlertMethodTypeDiscord,
v1alpha.AlertMethodTypeOpsgenie,
v1alpha.AlertMethodTypeServiceNow,
v1alpha.AlertMethodTypeJira,
v1alpha.AlertMethodTypeTeams,
v1alpha.AlertMethodTypeEmail,
}

var customAlertMethods = map[v1alpha.AlertMethodType][]alertMethodVariant{
var customAlertMethodsSubVariants = map[v1alpha.AlertMethodType][]alertMethodSpecSubVariant{
v1alpha.AlertMethodTypeWebhook: {
alertMethodVariantWebhookTemplate,
alertMethodVariantWebhookTemplateFields,
alertMethodSpecSubVariantWebhookTemplate,
alertMethodSpecSubVariantWebhookTemplateFields,
},
}

type AlertMethodVariant struct {
Type v1alpha.AlertMethodType
Variant alertMethodVariant
type alertMethodExample struct {
standardExample
methodType v1alpha.AlertMethodType
}

AlertMethod v1alphaAlertMethod.AlertMethod
func (a alertMethodExample) GetYAMLComments() []string {
comment := fmt.Sprintf("%s Alert Method", a.Variant)
if a.SubVariant != "" {
comment += fmt.Sprintf(" with %s", a.SubVariant)
}
return []string{comment}
}

func AlertMethod() []AlertMethodVariant {
variants := make([]AlertMethodVariant, 0, len(standardAlertMethods))
func AlertMethod() []Example {
variants := make([]alertMethodExample, 0, len(standardAlertMethods))
for _, typ := range standardAlertMethods {
variants = append(variants, AlertMethodVariant{
Type: typ,
variants = append(variants, alertMethodExample{
standardExample: standardExample{
Variant: typ.String(),
},
methodType: typ,
})
}
for typ, customVariants := range customAlertMethods {
for _, variant := range customVariants {
variants = append(variants, AlertMethodVariant{
Type: typ,
Variant: variant,
for typ, subVariants := range customAlertMethodsSubVariants {
for _, subVariant := range subVariants {
variants = append(variants, alertMethodExample{
standardExample: standardExample{
Variant: typ.String(),
SubVariant: subVariant,
},
methodType: typ,
})
}
}
for i := range variants {
variants[i].AlertMethod = variants[i].Generate()
variants[i].Object = variants[i].Generate()
}
return variants
return newExampleSlice(variants...)
}

func (a AlertMethodVariant) Generate() v1alphaAlertMethod.AlertMethod {
func (a alertMethodExample) Generate() v1alphaAlertMethod.AlertMethod {
am := v1alphaAlertMethod.New(
v1alphaAlertMethod.Metadata{
Name: strings.ToLower(a.Type.String()),
DisplayName: a.Type.String() + " Alert Method",
Name: strings.ToLower(a.Variant),
DisplayName: a.Variant + " Alert Method",
Project: sdk.DefaultProject,
},
v1alphaAlertMethod.Spec{
Description: fmt.Sprintf("Example %s Alert Method", a.Type),
Description: fmt.Sprintf("Example %s Alert Method", a.Variant),
},
)
return a.generateVariant(am)
}

func (a AlertMethodVariant) generateVariant(am v1alphaAlertMethod.AlertMethod) v1alphaAlertMethod.AlertMethod {
switch a.Type {
func (a alertMethodExample) generateVariant(am v1alphaAlertMethod.AlertMethod) v1alphaAlertMethod.AlertMethod {
switch a.methodType {
case v1alpha.AlertMethodTypeEmail:
am.Spec.Email = &v1alphaAlertMethod.EmailAlertMethod{
To: []string{"[email protected]"},
Expand Down Expand Up @@ -136,8 +157,8 @@ func (a AlertMethodVariant) generateVariant(am v1alphaAlertMethod.AlertMethod) v
},
},
}
switch a.Variant {
case alertMethodVariantWebhookTemplate:
switch a.SubVariant {
case alertMethodSpecSubVariantWebhookTemplate:
am.Spec.Webhook.Template = ptr(`{
"message": "Your SLO $slo_name needs attention!",
"timestamp": "$timestamp",
Expand All @@ -154,7 +175,7 @@ func (a AlertMethodVariant) generateVariant(am v1alphaAlertMethod.AlertMethod) v
"alert_policy": "$alert_policy_labels_text"
}
}`)
case alertMethodVariantWebhookTemplateFields:
case alertMethodSpecSubVariantWebhookTemplateFields:
am.Spec.Webhook.TemplateFields = []string{
"project_name",
"service_name",
Expand All @@ -170,7 +191,7 @@ func (a AlertMethodVariant) generateVariant(am v1alphaAlertMethod.AlertMethod) v
}
}
default:
panic(fmt.Sprintf("unexpected v1alpha.AlertMethodType: %#v", a.Type))
panic(fmt.Sprintf("unexpected v1alpha.AlertMethodType: %#v", a.Variant))
}
return am
}
19 changes: 19 additions & 0 deletions internal/manifest/v1alpha/examples/alert_method_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package v1alphaExamples

import (
"slices"
"testing"

"github.com/nobl9/nobl9-go/manifest/v1alpha"
)

func TestAlertMethod_SupportsAllAlertMethodTypes(t *testing.T) {
variants := AlertMethod()
for _, methodType := range v1alpha.AlertMethodTypeValues() {
if !slices.ContainsFunc(variants, func(e Example) bool {
return e.(alertMethodExample).methodType == methodType
}) {
t.Errorf("%T '%s' is not listed in the examples", methodType, methodType)
}
}
}
1 change: 1 addition & 0 deletions internal/manifest/v1alpha/examples/alert_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package v1alphaExamples
Loading

0 comments on commit 0b7dbc3

Please sign in to comment.