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

Commit

Permalink
fix: Use correct URLs in Keptn API clients (#783)
Browse files Browse the repository at this point in the history
* Introduce `keptn.ClientFactory`

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

* Cleanup environment variable variable names

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

* Move code to keptn package

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

* Unexport functions

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

* Revert back to `APIClient` for creating services

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

* Linting

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

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

serviceSynchronizer.Run()
onboard.NewDefaultServiceSynchronizer().Run()
}()
}

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

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

err = handler.HandleEvent()
err := event_handler.NewEventHandler(event).HandleEvent()
if err != nil {
log.WithError(err).Error("HandleEvent() returned an error")
}
Expand Down
32 changes: 0 additions & 32 deletions internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,11 @@ import (
log "github.com/sirupsen/logrus"

keptncommon "github.com/keptn/go-utils/pkg/lib"
"github.com/keptn/go-utils/pkg/lib/keptn"
)

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"

const configurationService = "CONFIGURATION_SERVICE"
const datastore = "DATASTORE"

const defaultShipyardControllerURL = "http://shipyard-controller:8080"

// GetConfigurationServiceURL Returns the endpoint to the configuration-service
func GetConfigurationServiceURL() string {
return getKeptnServiceURL(configurationService, keptn.ConfigurationServiceURL)
}

// GetDatastoreURL Returns the endpoint to the datastore
func GetDatastoreURL() string {
return getKeptnServiceURL(datastore, keptn.DatastoreURL)
}

// GetShipyardControllerURL Returns the endpoint to the shipyard-controller
func GetShipyardControllerURL() string {
return getKeptnServiceURL(ShipyardControllerURLEnvironmentVariableName, defaultShipyardControllerURL)
}

func getKeptnServiceURL(serviceName, defaultURL string) string {
url, err := keptn.GetServiceEndpoint(serviceName)
if err != nil {
return defaultURL
}
return url.String()
}

// DynatraceConfigDashboardQUERY defines the Dynatrace Configuration File structure and supporting Constants
const DynatraceConfigDashboardQUERY = "query"

Expand Down
43 changes: 19 additions & 24 deletions internal/event_handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ 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 @@ -27,24 +25,21 @@ type DynatraceEventHandler interface {
HandleEvent() error
}

// 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)
}
// NewEventHandler creates a new DynatraceEventHandler for the specified event.
func NewEventHandler(event cloudevents.Event) DynatraceEventHandler {
clientFactory := keptn.NewClientFactory()

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

return eventHandler, nil
return eventHandler
}

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

keptnEvent, err := getEventAdapter(event)
Expand All @@ -61,7 +56,7 @@ func getEventHandler(event cloudevents.Event, keptnAPISet api.APISet) (Dynatrace
return nil, errors.New("event has no project")
}

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

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

Expand Down
46 changes: 46 additions & 0 deletions internal/keptn/client_factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package keptn

import (
"net/http"

api "github.com/keptn/go-utils/pkg/api/utils"
)

// ClientFactoryInterface provides a factories for clients.
type ClientFactoryInterface interface {
CreateEventClient() EventClientInterface
CreateResourceClient() ResourceClientInterface
CreateServiceClient() ServiceClientInterface
CreateUniformClient() UniformClientInterface
}

// ClientFactory is an implementation of ClientFactoryInterface.
type ClientFactory struct {
}

// NewClientFactory creates a new ClientFactory.
func NewClientFactory() *ClientFactory {
return &ClientFactory{}
}

// CreateEventClient creates an EventClientInterface.
func (c *ClientFactory) CreateEventClient() EventClientInterface {
return NewEventClient(api.NewEventHandler(getDatastoreURL()))
}

// CreateResourceClient creates a ResourceClientInterface.
func (c *ClientFactory) CreateResourceClient() ResourceClientInterface {
return NewResourceClient(api.NewResourceHandler(getConfigurationServiceURL()))
}

// CreateServiceClient creates a ServiceClientInterface.
func (c *ClientFactory) CreateServiceClient() ServiceClientInterface {
return NewServiceClient(
api.NewServiceHandler(getShipyardControllerURL()),
&http.Client{})
}

// CreateUniformClient creates a UniformClientInterface.
func (c *ClientFactory) CreateUniformClient() UniformClientInterface {
return NewUniformClient(api.NewUniformHandler(getShipyardControllerURL()))
}
32 changes: 32 additions & 0 deletions internal/keptn/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package keptn

import "github.com/keptn/go-utils/pkg/lib/keptn"

const shipyardControllerURLEnvironmentVariableName = "SHIPYARD_CONTROLLER"
const configurationServiceEnvironmentVariableName = "CONFIGURATION_SERVICE"
const datastoreEnvironmentVariableName = "DATASTORE"

const defaultShipyardControllerURL = "http://shipyard-controller:8080"

// getConfigurationServiceURL Returns the endpoint to the configuration-service.
func getConfigurationServiceURL() string {
return getKeptnServiceURL(configurationServiceEnvironmentVariableName, keptn.ConfigurationServiceURL)
}

// getDatastoreURL Returns the endpoint to the datastore.
func getDatastoreURL() string {
return getKeptnServiceURL(datastoreEnvironmentVariableName, keptn.DatastoreURL)
}

// getShipyardControllerURL Returns the endpoint to the shipyard-controller.
func getShipyardControllerURL() string {
return getKeptnServiceURL(shipyardControllerURLEnvironmentVariableName, defaultShipyardControllerURL)
}

func getKeptnServiceURL(serviceName, defaultURL string) string {
url, err := keptn.GetServiceEndpoint(serviceName)
if err != nil {
return defaultURL
}
return url.String()
}
86 changes: 86 additions & 0 deletions internal/keptn/keptn_api_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package keptn

import (
"encoding/json"
"fmt"

"github.com/keptn-contrib/dynatrace-service/internal/rest"
)

// APIClientInterface provides methods for accessing a Keptn API.
type APIClientInterface interface {
// Post performs a post request and returns a validated response or an error.
Post(apiPath string, body []byte) ([]byte, error)
}

// APIClient is an implementation of APIClientInterface using a rest.ClientInterface.
type APIClient struct {
restClient rest.ClientInterface
}

// NewAPIClient creates a new APIClient.
func NewAPIClient(client rest.ClientInterface) *APIClient {
return &APIClient{
restClient: client,
}
}

// Post performs a post request and returns a validated response or an error.
func (c *APIClient) Post(apiPath string, body []byte) ([]byte, error) {
body, status, url, err := c.restClient.Post(apiPath, body)
if err != nil {
return nil, err
}

return validateResponse(body, status, url)
}

// genericAPIErrorDTO will support multiple Keptn API errors
type genericAPIErrorDTO struct {
Code int `json:"code"`
ErrorCode int `json:"errorCode"`
Message string `json:"message"`
}

func (e *genericAPIErrorDTO) status() int {
if e.Code != 0 {
return e.Code
}

return e.ErrorCode
}

// APIError respresents an error returned from a Keptn API.
type APIError struct {
status int
message string
url string
}

// Error returns a string representation of the APIError.
func (e *APIError) Error() string {
return fmt.Sprintf("Keptn API error (%d): %s - URL: %s", e.status, e.message, e.url)
}

func validateResponse(body []byte, status int, url string) ([]byte, error) {
if status < 200 || status >= 300 {
// try to get the error information
keptnAPIError := &genericAPIErrorDTO{}
err := json.Unmarshal(body, keptnAPIError)
if err != nil {
return body, &APIError{
status: status,
message: string(body),
url: url,
}
}

return nil, &APIError{
status: keptnAPIError.status(),
message: keptnAPIError.Message,
url: url,
}
}

return body, nil
}
5 changes: 2 additions & 3 deletions internal/keptn/keptn_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/cloudevents/sdk-go/v2/event"
"github.com/keptn-contrib/dynatrace-service/internal/adapter"
"github.com/keptn-contrib/dynatrace-service/internal/common"
keptnapi "github.com/keptn/go-utils/pkg/lib/keptn"
keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0"
)
Expand Down Expand Up @@ -87,8 +86,8 @@ func NewClient(client *keptnv2.Keptn) *Client {

func NewDefaultClient(event event.Event) (*Client, error) {
keptnOpts := keptnapi.KeptnOpts{
ConfigurationServiceURL: common.GetConfigurationServiceURL(),
DatastoreURL: common.GetDatastoreURL(),
ConfigurationServiceURL: getConfigurationServiceURL(),
DatastoreURL: getDatastoreURL(),
}
kClient, err := keptnv2.NewKeptn(&event, keptnOpts)
if err != nil {
Expand Down
Loading

0 comments on commit 479d2f2

Please sign in to comment.