Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Commit

Permalink
feat: Use keptn/go-utils to access Keptn APIs (#767)
Browse files Browse the repository at this point in the history
* Use goutils APIs in UniformClient

Signed-off-by: Arthur Pitman <[email protected]>

* Add test for `GetIntegrationIDByName`

Signed-off-by: Arthur Pitman <[email protected]>

* Use goutils APIs in ServiceClient

Signed-off-by: Arthur Pitman <[email protected]>

* Use goutils APIs in ResourceClient and ConfigResourceClient

Signed-off-by: Arthur Pitman <[email protected]>

* Remove unused LocalResourceClient

Signed-off-by: Arthur Pitman <[email protected]>

* Use goutils APIs in EventClient

Signed-off-by: Arthur Pitman <[email protected]>

* Remove unused APIClient

Signed-off-by: Arthur Pitman <[email protected]>

* Linting

Signed-off-by: Arthur Pitman <[email protected]>

* Method for creating integrations in test

Signed-off-by: Arthur Pitman <[email protected]>

* Remove redundant `EventClientBaseInterface`

Signed-off-by: Arthur Pitman <[email protected]>

* Wrap error

Signed-off-by: Arthur Pitman <[email protected]>

* Remove `keptn` from filenames

Signed-off-by: Arthur Pitman <[email protected]>

* Renaming types

Signed-off-by: Arthur Pitman <[email protected]>

* Rename files

Signed-off-by: Arthur Pitman <[email protected]>

* Fix error check

Signed-off-by: Arthur Pitman <[email protected]>

* Remove redundant check

Signed-off-by: Arthur Pitman <[email protected]>

* Shorten error messages

Signed-off-by: Arthur Pitman <[email protected]>

* Improve problem ID parsing

Signed-off-by: Arthur Pitman <[email protected]>

* Rename constant

Signed-off-by: Arthur Pitman <[email protected]>

* Extract function for getting problem ID from labels

Signed-off-by: Arthur Pitman <[email protected]>

* Extract problem ID from URL fragment and add test

Signed-off-by: Arthur Pitman <[email protected]>
  • Loading branch information
arthurpitman authored Apr 12, 2022
1 parent 5baa6f5 commit a2a798a
Show file tree
Hide file tree
Showing 30 changed files with 567 additions and 501 deletions.
16 changes: 14 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ func _main(args []string, envCfg envConfig) int {

if env.IsServiceSyncEnabled() {
go func() {
onboard.NewDefaultServiceSynchronizer().Run()
serviceSynchronizer, err := onboard.NewDefaultServiceSynchronizer()
if err != nil {
log.WithError(err).Error("Could not create service synchronizer")
return
}

serviceSynchronizer.Run()
}()
}

Expand All @@ -58,7 +64,13 @@ func _main(args []string, envCfg envConfig) int {
}

func gotEvent(ctx context.Context, event cloudevents.Event) error {
err := event_handler.NewEventHandler(event).HandleEvent()
handler, err := event_handler.NewEventHandler(event)
if err != nil {
log.WithError(err).Error("Could not create event handler")
return err
}

err = handler.HandleEvent()
if err != nil {
log.WithError(err).Error("HandleEvent() returned an error")
}
Expand Down
3 changes: 2 additions & 1 deletion internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (
"github.com/keptn/go-utils/pkg/lib/keptn"
)

const KEPTNSBRIDGE_LABEL = "Keptns Bridge"
const BridgeLabel = "Keptns Bridge"
const ProblemURLLabel = "Problem URL"

// ShipyardControllerURLEnvironmentVariableName is the name of the environment variable for specifying the shipyard controller URL.
const ShipyardControllerURLEnvironmentVariableName = "SHIPYARD_CONTROLLER"
Expand Down
4 changes: 2 additions & 2 deletions internal/config/dynatrace_config_getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ type DynatraceConfigProvider interface {
}

type DynatraceConfigGetter struct {
resourceClient keptn.DynatraceConfigResourceClientInterface
resourceClient keptn.DynatraceConfigReaderInterface
}

func NewDynatraceConfigGetter(client keptn.DynatraceConfigResourceClientInterface) *DynatraceConfigGetter {
func NewDynatraceConfigGetter(client keptn.DynatraceConfigReaderInterface) *DynatraceConfigGetter {
return &DynatraceConfigGetter{
resourceClient: client,
}
Expand Down
3 changes: 2 additions & 1 deletion internal/deployment/evaluation_finished_event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package deployment

import (
"fmt"

"github.com/keptn-contrib/dynatrace-service/internal/common"
"github.com/keptn-contrib/dynatrace-service/internal/dynatrace"
"github.com/keptn-contrib/dynatrace-service/internal/keptn"
Expand Down Expand Up @@ -50,7 +51,7 @@ func (eh *EvaluationFinishedEventHandler) HandleEvent() error {
pid, err := eh.eClient.FindProblemID(eh.event)
if err == nil && pid != "" {
// Comment we push over
comment := fmt.Sprintf("[Keptn remediation evaluation](%s) resulted in %s (%.2f/100)", eh.event.GetLabels()[common.KEPTNSBRIDGE_LABEL], eh.event.GetResult(), eh.event.GetEvaluationScore())
comment := fmt.Sprintf("[Keptn remediation evaluation](%s) resulted in %s (%.2f/100)", eh.event.GetLabels()[common.BridgeLabel], eh.event.GetResult(), eh.event.GetEvaluationScore())

// this is posting the Event on the problem as a comment
dynatrace.NewProblemsClient(eh.dtClient).AddProblemComment(pid, comment)
Expand Down
17 changes: 10 additions & 7 deletions internal/event_handler/error_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ import (
"github.com/keptn-contrib/dynatrace-service/internal/sli"
)

// ErrorHandler handles errors by trying to send them to Keptn Uniform.
type ErrorHandler struct {
err error
evt cloudevents.Event
err error
evt cloudevents.Event
uniformClient keptn.UniformClientInterface
}

func NewErrorHandler(err error, event cloudevents.Event) *ErrorHandler {
// NewErrorHandler creates a new ErrorHandler for the specified error, event and UniformClientInterface.
func NewErrorHandler(err error, event cloudevents.Event, uniformClient keptn.UniformClientInterface) *ErrorHandler {
return &ErrorHandler{
err: err,
evt: event,
err: err,
evt: event,
uniformClient: uniformClient,
}
}

Expand Down Expand Up @@ -59,8 +63,7 @@ func (eh ErrorHandler) sendErroredGetSLIFinishedEvent(keptnClient *keptn.Client)
}

func (eh ErrorHandler) sendErrorEvent(keptnClient *keptn.Client) error {
uniformClient := keptn.NewDefaultUniformClient()
integrationID, err := uniformClient.GetIntegrationIDFor(event.GetEventSource())
integrationID, err := eh.uniformClient.GetIntegrationIDByName(event.GetEventSource())
if err != nil {
log.WithError(err).Error("Could not retrieve integration ID from Keptn Uniform")
// no need to continue here, message will not show up in Uniform
Expand Down
43 changes: 26 additions & 17 deletions internal/event_handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"fmt"

cloudevents "github.com/cloudevents/sdk-go/v2"
api "github.com/keptn/go-utils/pkg/api/utils"
keptnevents "github.com/keptn/go-utils/pkg/lib"
keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0"
log "github.com/sirupsen/logrus"

"github.com/keptn-contrib/dynatrace-service/internal/adapter"
"github.com/keptn-contrib/dynatrace-service/internal/common"
"github.com/keptn-contrib/dynatrace-service/internal/config"
"github.com/keptn-contrib/dynatrace-service/internal/credentials"
"github.com/keptn-contrib/dynatrace-service/internal/deployment"
Expand All @@ -20,22 +22,29 @@ import (
"github.com/keptn-contrib/dynatrace-service/internal/sli"
)

// DynatraceEventHandler is the common interface for all event handlers.
type DynatraceEventHandler interface {
HandleEvent() error
}

func NewEventHandler(event cloudevents.Event) DynatraceEventHandler {
eventHandler, err := getEventHandler(event)
// NewEventHandler creates a new DynatraceEventHandler for the specified event or returns an error.
func NewEventHandler(event cloudevents.Event) (DynatraceEventHandler, error) {
keptnAPISet, err := api.New(common.GetShipyardControllerURL())
if err != nil {
return nil, fmt.Errorf("could not create Keptn API set: %w", err)
}

eventHandler, err := getEventHandler(event, *keptnAPISet)
if err != nil {
err = fmt.Errorf("cannot handle event: %w", err)
log.Error(err.Error())
return NewErrorHandler(err, event)
return NewErrorHandler(err, event, keptn.NewUniformClient(keptnAPISet.UniformV1())), nil
}

return eventHandler
return eventHandler, nil
}

func getEventHandler(event cloudevents.Event) (DynatraceEventHandler, error) {
func getEventHandler(event cloudevents.Event, keptnAPISet api.APISet) (DynatraceEventHandler, error) {
log.WithField("eventType", event.Type()).Debug("Received event")

keptnEvent, err := getEventAdapter(event)
Expand All @@ -52,7 +61,7 @@ func getEventHandler(event cloudevents.Event) (DynatraceEventHandler, error) {
return nil, errors.New("event has no project")
}

dynatraceConfigGetter := config.NewDynatraceConfigGetter(keptn.NewDefaultResourceClient())
dynatraceConfigGetter := config.NewDynatraceConfigGetter(keptn.NewConfigClient(keptn.NewResourceClient(keptnAPISet.ResourcesV1())))
dynatraceConfig, err := dynatraceConfigGetter.GetDynatraceConfig(keptnEvent)
if err != nil {
return nil, fmt.Errorf("could not get configuration: %w", err)
Expand All @@ -77,29 +86,29 @@ func getEventHandler(event cloudevents.Event) (DynatraceEventHandler, error) {

switch aType := keptnEvent.(type) {
case *monitoring.ConfigureMonitoringAdapter:
return monitoring.NewConfigureMonitoringEventHandler(keptnEvent.(*monitoring.ConfigureMonitoringAdapter), dtClient, kClient, keptn.NewDefaultResourceClient(), keptn.NewDefaultServiceClient()), nil
return monitoring.NewConfigureMonitoringEventHandler(keptnEvent.(*monitoring.ConfigureMonitoringAdapter), dtClient, kClient, keptn.NewConfigClient(keptn.NewResourceClient(keptnAPISet.ResourcesV1())), keptn.NewServiceClient(keptnAPISet.ServicesV1(), keptnAPISet.APIV1())), nil
case *problem.ProblemAdapter:
return problem.NewProblemEventHandler(keptnEvent.(*problem.ProblemAdapter), kClient), nil
case *problem.ActionTriggeredAdapter:
return problem.NewActionTriggeredEventHandler(keptnEvent.(*problem.ActionTriggeredAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return problem.NewActionTriggeredEventHandler(keptnEvent.(*problem.ActionTriggeredAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
case *problem.ActionStartedAdapter:
return problem.NewActionStartedEventHandler(keptnEvent.(*problem.ActionStartedAdapter), dtClient, keptn.NewDefaultEventClient()), nil
return problem.NewActionStartedEventHandler(keptnEvent.(*problem.ActionStartedAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1())), nil
case *problem.ActionFinishedAdapter:
return problem.NewActionFinishedEventHandler(keptnEvent.(*problem.ActionFinishedAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return problem.NewActionFinishedEventHandler(keptnEvent.(*problem.ActionFinishedAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
case *sli.GetSLITriggeredAdapter:
return sli.NewGetSLITriggeredHandler(keptnEvent.(*sli.GetSLITriggeredAdapter), dtClient, kClient, keptn.NewDefaultResourceClient(), dynatraceConfig.DtCreds, dynatraceConfig.Dashboard), nil
return sli.NewGetSLITriggeredHandler(keptnEvent.(*sli.GetSLITriggeredAdapter), dtClient, kClient, keptn.NewConfigClient(keptn.NewResourceClient(keptnAPISet.ResourcesV1())), dynatraceConfig.DtCreds, dynatraceConfig.Dashboard), nil
case *deployment.DeploymentFinishedAdapter:
return deployment.NewDeploymentFinishedEventHandler(keptnEvent.(*deployment.DeploymentFinishedAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return deployment.NewDeploymentFinishedEventHandler(keptnEvent.(*deployment.DeploymentFinishedAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
case *deployment.TestTriggeredAdapter:
return deployment.NewTestTriggeredEventHandler(keptnEvent.(*deployment.TestTriggeredAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return deployment.NewTestTriggeredEventHandler(keptnEvent.(*deployment.TestTriggeredAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
case *deployment.TestFinishedAdapter:
return deployment.NewTestFinishedEventHandler(keptnEvent.(*deployment.TestFinishedAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return deployment.NewTestFinishedEventHandler(keptnEvent.(*deployment.TestFinishedAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
case *deployment.EvaluationFinishedAdapter:
return deployment.NewEvaluationFinishedEventHandler(keptnEvent.(*deployment.EvaluationFinishedAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return deployment.NewEvaluationFinishedEventHandler(keptnEvent.(*deployment.EvaluationFinishedAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
case *deployment.ReleaseTriggeredAdapter:
return deployment.NewReleaseTriggeredEventHandler(keptnEvent.(*deployment.ReleaseTriggeredAdapter), dtClient, keptn.NewDefaultEventClient(), dynatraceConfig.AttachRules), nil
return deployment.NewReleaseTriggeredEventHandler(keptnEvent.(*deployment.ReleaseTriggeredAdapter), dtClient, keptn.NewEventClient(keptnAPISet.EventsV1()), dynatraceConfig.AttachRules), nil
default:
return NewErrorHandler(fmt.Errorf("this should not have happened, we are missing an implementation for: %T", aType), event), nil
return NewErrorHandler(fmt.Errorf("this should not have happened, we are missing an implementation for: %T", aType), event, keptn.NewUniformClient(keptnAPISet.UniformV1())), nil
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,29 @@ import (
"github.com/keptn-contrib/dynatrace-service/internal/dynatrace"
)

type SLOResourceReaderInterface interface {
// SLOReaderInterface provides functionality for getting SLOs.
type SLOReaderInterface interface {
// GetSLOs gets the SLOs stored for exactly the specified project, stage and service.
GetSLOs(project string, stage string, service string) (*keptn.ServiceLevelObjectives, error)
}
type SLIAndSLOResourceWriterInterface interface {

// SLIAndSLOWriterInterface provides functionality for uploading SLIs and SLOs.
type SLIAndSLOWriterInterface interface {
// UploadSLIs uploads the SLIs for the specified project, stage and service.
UploadSLIs(project string, stage string, service string, slis *dynatrace.SLI) error

// UploadSLOs uploads the SLOs for the specified project, stage and service.
UploadSLOs(project string, stage string, service string, slos *keptn.ServiceLevelObjectives) error
}
type ResourceClientInterface interface {
SLOResourceReaderInterface
SLIAndSLOResourceWriterInterface

// SLOAndSLIClientInterface provides functionality for getting SLOs and uploading SLIs and SLOs.
type SLOAndSLIClientInterface interface {
SLOReaderInterface
SLIAndSLOWriterInterface
}

type DynatraceConfigResourceClientInterface interface {
// DynatraceConfigReaderInterface provides functionality for getting a Dynatrace config.
type DynatraceConfigReaderInterface interface {
// GetDynatraceConfig gets the Dynatrace config for the specified project, stage and service, checking first on the service, then stage and then project level.
GetDynatraceConfig(project string, stage string, service string) (string, error)
}
Expand All @@ -35,25 +41,20 @@ const sloFilename = "slo.yaml"
const sliFilename = "dynatrace/sli.yaml"
const configFilename = "dynatrace/dynatrace.conf.yaml"

// ResourceClient is the default implementation for the *ResourceClientInterfaces using a ConfigResourceClientInterface
type ResourceClient struct {
client ConfigResourceClientInterface
}

// NewDefaultResourceClient creates a new ResourceClient with a default Keptn resource handler for the configuration service
func NewDefaultResourceClient() *ResourceClient {
return NewResourceClient(
NewDefaultConfigResourceClient())
// ConfigClient is the default implementation for ResourceClientInterface using a ConfigResourceClientInterface.
type ConfigClient struct {
client ResourceClientInterface
}

// NewResourceClient creates a new ResourceClient with a Keptn resource handler for the configuration service
func NewResourceClient(client ConfigResourceClientInterface) *ResourceClient {
return &ResourceClient{
// NewConfigClient creates a new ConfigClient with a Keptn resource handler for the configuration service.
func NewConfigClient(client ResourceClientInterface) *ConfigClient {
return &ConfigClient{
client: client,
}
}

func (rc *ResourceClient) GetSLOs(project string, stage string, service string) (*keptn.ServiceLevelObjectives, error) {
// GetSLOs gets the SLOs stored for exactly the specified project, stage and service.
func (rc *ConfigClient) GetSLOs(project string, stage string, service string) (*keptn.ServiceLevelObjectives, error) {
resource, err := rc.client.GetServiceResource(project, stage, service, sloFilename)
if err != nil {
return nil, err
Expand All @@ -68,8 +69,8 @@ func (rc *ResourceClient) GetSLOs(project string, stage string, service string)
return slos, nil
}

func (rc *ResourceClient) UploadSLOs(project string, stage string, service string, slos *keptn.ServiceLevelObjectives) error {
// and now we save it back to Keptn
// UploadSLOs uploads the SLOs for the specified project, stage and service.
func (rc *ConfigClient) UploadSLOs(project string, stage string, service string, slos *keptn.ServiceLevelObjectives) error {
yamlAsByteArray, err := yaml.Marshal(slos)
if err != nil {
return fmt.Errorf("could not convert SLOs to YAML: %s", err)
Expand All @@ -78,7 +79,8 @@ func (rc *ResourceClient) UploadSLOs(project string, stage string, service strin
return rc.client.UploadResource(yamlAsByteArray, sloFilename, project, stage, service)
}

func (rc *ResourceClient) UploadSLIs(project string, stage string, service string, slis *dynatrace.SLI) error {
// UploadSLIs uploads the SLIs for the specified project, stage and service.
func (rc *ConfigClient) UploadSLIs(project string, stage string, service string, slis *dynatrace.SLI) error {
yamlAsByteArray, err := yaml.Marshal(slis)
if err != nil {
return fmt.Errorf("could not convert SLIs to YAML: %s", err)
Expand All @@ -87,6 +89,7 @@ func (rc *ResourceClient) UploadSLIs(project string, stage string, service strin
return rc.client.UploadResource(yamlAsByteArray, sliFilename, project, stage, service)
}

func (rc *ResourceClient) GetDynatraceConfig(project string, stage string, service string) (string, error) {
// GetDynatraceConfig gets the Dynatrace config for the specified project, stage and service, checking first on the service, then stage and then project level.
func (rc *ConfigClient) GetDynatraceConfig(project string, stage string, service string) (string, error) {
return rc.client.GetResource(project, stage, service, configFilename)
}
Loading

0 comments on commit a2a798a

Please sign in to comment.