Skip to content

Commit

Permalink
Add Contact Points
Browse files Browse the repository at this point in the history
  • Loading branch information
safaci2000 committed Sep 12, 2024
1 parent 5e5f2a2 commit 62e39cf
Show file tree
Hide file tree
Showing 11 changed files with 628 additions and 2 deletions.
34 changes: 34 additions & 0 deletions cli/backup/alerting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package backup

import (
"context"

"github.com/bep/simplecobra"
"github.com/esnet/gdg/cli/support"
"github.com/spf13/cobra"
)

func newAlertingCommand() simplecobra.Commander {
description := "Manage Alerting resources"
return &support.SimpleCommand{
NameP: "alerting",
Short: description,
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"alert"}
// connections := cmd
// connections.PersistentFlags().StringP("connection", "", "", "filter by connection slug")
},
CommandsList: []simplecobra.Commander{
newAlertingContactCommand(),
// newClearConnectionsCmd(),
// newUploadConnectionsCmd(),
// newDownloadConnectionsCmd(),
// newListConnectionsCmd(),
// newConnectionsPermissionCmd(),
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
return cd.CobraCommand.Help()
},
}
}
148 changes: 148 additions & 0 deletions cli/backup/alerting_contacts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package backup

import (
"context"
"encoding/json"
"log/slog"

"github.com/bep/simplecobra"
"github.com/esnet/gdg/cli/support"
"github.com/esnet/gdg/internal/service"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)

func newAlertingContactCommand() simplecobra.Commander {
description := "Manage Alerting ContactPoints "
return &support.SimpleCommand{
NameP: "contactpoint",
Short: description,
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"contact", "contacts", "contactpoints"}
},
CommandsList: []simplecobra.Commander{
newListContactPointsCmd(),
newClearContactPointsCmd(),
newUploadContactPointsCmd(),
newDownloadContactPointsCmd(),
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
return cd.CobraCommand.Help()
},
}
}

func newListContactPointsCmd() simplecobra.Commander {
description := "List all contact points for the given Organization"
return &support.SimpleCommand{
NameP: "list",
Short: description,
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"l"}
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
rootCmd.TableObj.AppendHeader(table.Row{"uid", "name", "slug", "type", "provenance", "settings"})
contactPoints := rootCmd.GrafanaSvc().ListContactPoints()
slog.Info("Listing contact points for context",
slog.String("Organization", GetOrganizationName()),
slog.String("context", GetContext()))
if len(contactPoints) == 0 {
slog.Info("No contact points found")
} else {
for _, link := range contactPoints {
rawBytes, err := json.Marshal(link.Settings)
if err != nil {
slog.Warn("unable to marshall settings to valid JSON")
}
rootCmd.TableObj.AppendRow(table.Row{link.UID, link.Name, service.GetSlug(link.Name), link.Type, link.Provenance, string(rawBytes)})
}
rootCmd.Render(cd.CobraCommand, contactPoints)
}
return nil
},
}
}

func newDownloadContactPointsCmd() simplecobra.Commander {
description := "Download all contact points for the given Organization"
return &support.SimpleCommand{
NameP: "download",
Short: description,
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"d"}
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
file, err := rootCmd.GrafanaSvc().DownloadContactPoints()
slog.Info("Download contact points for context",
slog.String("Organization", GetOrganizationName()),
slog.String("context", GetContext()))
if err != nil {
slog.Error("unable to contact point")
} else {
slog.Info("contact points successfully downloaded", slog.Any("file", file))
}
return nil
},
}
}

func newUploadContactPointsCmd() simplecobra.Commander {
description := "Upload all contact points for the given Organization"
return &support.SimpleCommand{
NameP: "upload",
Short: description,
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"u"}
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
removedItems, err := rootCmd.GrafanaSvc().UploadContactPoints()
slog.Info("Upload contact points for context",
slog.String("Organization", GetOrganizationName()),
slog.String("context", GetContext()))
if err != nil {
slog.Error("unable to upload contact points", slog.Any("err", err))
} else {
slog.Info("contact points successfully uploaded")
rootCmd.TableObj.AppendHeader(table.Row{"name"})
for _, item := range removedItems {
rootCmd.TableObj.AppendRow(table.Row{item})
}

rootCmd.Render(cd.CobraCommand, removedItems)
}
return nil
},
}
}

func newClearContactPointsCmd() simplecobra.Commander {
description := "Clear all contact points for the given Organization"
return &support.SimpleCommand{
NameP: "clear",
Short: description,
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"l"}
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
removedItems, err := rootCmd.GrafanaSvc().ClearContactPoints()
slog.Info("Clear contact points for context",
slog.String("Organization", GetOrganizationName()),
slog.String("context", GetContext()))
if err != nil {
slog.Error("unable to contact point")
} else {
slog.Info("contact points successfully removed")
rootCmd.TableObj.AppendHeader(table.Row{"name"})
for _, item := range removedItems {
rootCmd.TableObj.AppendRow(table.Row{item})
}
}
return nil
},
}
}
1 change: 1 addition & 0 deletions cli/backup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ limited to clear/delete, list, download and upload. Any other functionality wil
newOrganizationsCommand(),
newTeamsCommand(),
newUsersCommand(),
newAlertingCommand(),
},
}
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.16.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
go.opentelemetry.io/otel v1.30.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,12 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
Expand Down
2 changes: 2 additions & 0 deletions internal/config/resource_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
UserResource ResourceType = "users"
TemplatesResource ResourceType = "templates"
SecureSecretsResource ResourceType = "secure"
AlertingResource ResourceType = "alerting"
)

var orgNamespacedResource = map[ResourceType]bool{
Expand All @@ -32,6 +33,7 @@ var orgNamespacedResource = map[ResourceType]bool{
FolderResource: true,
LibraryElementResource: true,
TeamResource: true,
AlertingResource: true,
}

// isNamespaced returns true if the resource type is namespaced
Expand Down
133 changes: 133 additions & 0 deletions internal/service/alerting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package service

import (
"encoding/json"
"fmt"
"log"
"log/slog"

"github.com/esnet/gdg/internal/config"
"github.com/esnet/gdg/internal/tools"
"github.com/grafana/grafana-openapi-client-go/client/provisioning"
"github.com/grafana/grafana-openapi-client-go/models"
)

func (s *DashNGoImpl) ListContactPoints() []*models.EmbeddedContactPoint {
p := provisioning.NewGetContactpointsParams()
result, err := s.GetClient().Provisioning.GetContactpoints(p)
if err != nil {
log.Fatalf("unable to retrieve contact points, err:%s", err.Error())
}
return result.GetPayload()
}

func (s *DashNGoImpl) DownloadContactPoints() (string, error) {
var (
dsPacked []byte
err error
)
p := provisioning.NewGetContactpointsExportParams()
p.Download = tools.PtrOf(true)
p.Decrypt = tools.PtrOf(true)
p.Format = tools.PtrOf("json")
data, err := s.GetClient().Provisioning.GetContactpointsExport(p)
if err != nil {
log.Fatalf("unable to retrieve Contact Points, err: %s", err.Error())
}

dsPath := buildResourcePath("contacts", config.AlertingResource)
if dsPacked, err = json.MarshalIndent(data.GetPayload(), "", " "); err != nil {
return "", fmt.Errorf("unable to serialize data to JSON. %w", err)
}
if err = s.storage.WriteFile(dsPath, dsPacked); err != nil {
return "", fmt.Errorf("unable to write file. %w", err)
}

return dsPath, nil
}

func (s *DashNGoImpl) UploadContactPoints() ([]string, error) {
var (
err error
rawDS []byte
result []string
)
data := new(models.AlertingFileExport)
currentContacts := s.ListContactPoints()
m := make(map[string]*models.EmbeddedContactPoint)
for ndx, i := range currentContacts {
m[i.UID] = currentContacts[ndx]
}

fileLocation := buildResourcePath("contacts", config.AlertingResource)
if rawDS, err = s.storage.ReadFile(fileLocation); err != nil {
return nil, fmt.Errorf("failed to read file. file: %s, err: %w", fileLocation, err)
}
if err = json.Unmarshal(rawDS, data); err != nil {
return nil, fmt.Errorf("failed to unmarshall file, file:%s, err: %w", fileLocation, err)
}
for _, i := range data.ContactPoints {
for _, r := range i.Receivers {
if _, ok := m[r.UID]; ok {
// do update
p := provisioning.NewPutContactpointParams()
p.UID = r.UID
p.Body = &models.EmbeddedContactPoint{
DisableResolveMessage: false,
Name: i.Name,
Provenance: "",
Settings: r.Settings,
Type: tools.PtrOf(r.Type),
UID: r.UID,
}
_, err := s.GetClient().Provisioning.PutContactpoint(p)
if err != nil {
slog.Error("failed to update contact point", slog.Any("uid", r.UID))
continue
}
result = append(result, i.Name)

} else {
p := provisioning.NewPostContactpointsParams()
p.Body = &models.EmbeddedContactPoint{
DisableResolveMessage: false,
Name: i.Name,
UID: r.UID,
Provenance: "",
Settings: r.Settings,
Type: tools.PtrOf(r.Type),
}
_, err := s.GetClient().Provisioning.PostContactpoints(p)
if err != nil {
slog.Error("failed to create contact point", slog.Any("uid", r.UID))
continue
}

result = append(result, i.Name)
}
}
}

return result, nil
}

func (s *DashNGoImpl) ClearContactPoints() ([]string, error) {
var (
err error
results []string
)
contacts := s.ListContactPoints()
for _, contact := range contacts {
_, err = s.GetClient().Provisioning.DeleteContactpoints(contact.UID)
if err != nil {
slog.Error("unable to delete contact point",
slog.Any("name", contact.Name),
slog.Any("uid", contact.UID),
)
continue
}
results = append(results, contact.Name)
}

return results, nil
}
8 changes: 8 additions & 0 deletions internal/service/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type GrafanaService interface {
FoldersApi
LibraryElementsApi
TeamsApi
AlertingApi

AuthenticationApi
// MetaData
Expand Down Expand Up @@ -57,6 +58,13 @@ type DashboardsApi interface {
LintDashboards(req types.LintRequest) []string
}

type AlertingApi interface {
ListContactPoints() []*models.EmbeddedContactPoint
DownloadContactPoints() (string, error)
ClearContactPoints() ([]string, error)
UploadContactPoints() ([]string, error)
}

// FoldersApi Contract definition
type FoldersApi interface {
ListFolders(filter filters.Filter) []*customModels.FolderDetails
Expand Down
Loading

0 comments on commit 62e39cf

Please sign in to comment.