Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Commit

Permalink
feat: Move commonly used modules from keptn/keptn into sub-packages o…
Browse files Browse the repository at this point in the history
…f go-utils (#483)

* feat: Move commonly used modules from keptn/keptn into sub-packages of go-utils

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>

* feat: Move commonly used modules from keptn/keptn into sub-packages of go-utils

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>

* merge master

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>

* move internal api client utils to api package

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>

* try to fix sync issue

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>

* remove -race flag from test

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
bacherfl committed Jun 28, 2022
1 parent 8d145bc commit 3ed2fc6
Show file tree
Hide file tree
Showing 50 changed files with 6,493 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Test
run: go test -race -v ./...
run: go test -v ./...
29 changes: 20 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ module github.com/keptn/go-utils
go 1.17

require (
github.com/avast/retry-go v3.0.0+incompatible
github.com/benbjohnson/clock v1.3.0
github.com/cloudevents/sdk-go/observability/opentelemetry/v2 v2.0.0-20211001212819-74757a691209
github.com/cloudevents/sdk-go/v2 v2.9.0
github.com/cloudevents/sdk-go/v2 v2.10.0
github.com/google/uuid v1.3.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/nats-io/nats-server/v2 v2.8.4
github.com/nats-io/nats.go v1.16.0
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0
go.opentelemetry.io/otel v1.2.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0
go.opentelemetry.io/otel v1.7.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0
go.opentelemetry.io/otel/sdk v1.2.0
go.opentelemetry.io/otel/trace v1.2.0
go.opentelemetry.io/otel/trace v1.7.0
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.22.11
k8s.io/apimachinery v0.22.11
Expand All @@ -25,32 +31,37 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/go-logr/logr v1.2.2 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.14.4 // indirect
github.com/minio/highwayhash v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a // indirect
github.com/nats-io/nkeys v0.3.0 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/onsi/gomega v1.15.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 // indirect
go.opentelemetry.io/otel/internal/metric v0.25.0 // indirect
go.opentelemetry.io/otel/metric v0.25.0 // indirect
go.opentelemetry.io/otel/metric v0.30.0 // indirect
go.opentelemetry.io/proto/otlp v0.10.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.0 // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
Expand Down
60 changes: 46 additions & 14 deletions go.sum

Large diffs are not rendered by default.

238 changes: 238 additions & 0 deletions pkg/api/utils/internal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
package api

import (
"github.com/benbjohnson/clock"
"github.com/keptn/go-utils/pkg/api/models"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"net/http"
"time"
)

// InternalAPISet is an implementation of APISet
// which can be used from within the Keptn control plane
type InternalAPISet struct {
apimap InClusterAPIMappings
httpClient *http.Client
apiHandler *InternalAPIHandler
authHandler *AuthHandler
eventHandler *EventHandler
logHandler *LogHandler
projectHandler *ProjectHandler
resourceHandler *ResourceHandler
secretHandler *SecretHandler
sequenceControlHandler *SequenceControlHandler
serviceHandler *ServiceHandler
stageHandler *StageHandler
uniformHandler *UniformHandler
shipyardControlHandler *ShipyardControllerHandler
}

// InternalService is used to enumerate internal Keptn services
type InternalService int

const (
ConfigurationService InternalService = iota
ShipyardController
ApiService
SecretService
MongoDBDatastore
)

// InClusterAPIMappings maps a keptn service name to its reachable domain name
type InClusterAPIMappings map[InternalService]string

// DefaultInClusterAPIMappings gives you the default InClusterAPIMappings
var DefaultInClusterAPIMappings = InClusterAPIMappings{
ConfigurationService: "configuration-service:8080",
ShipyardController: "shipyard-controller:8080",
ApiService: "api-service:8080",
SecretService: "secret-service:8080",
MongoDBDatastore: "mongodb-datastore:8080",
}

// NewInternal creates a new InternalAPISet usable for calling keptn services from within the control plane
func NewInternal(client *http.Client, apiMappings ...InClusterAPIMappings) (*InternalAPISet, error) {
var apimap InClusterAPIMappings
if len(apiMappings) > 0 {
apimap = apiMappings[0]
} else {
apimap = DefaultInClusterAPIMappings
}

if client == nil {
client = &http.Client{}
}

as := &InternalAPISet{}
as.httpClient = client

as.apiHandler = &InternalAPIHandler{
shipyardControllerApiHandler: &APIHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
},
}

as.authHandler = &AuthHandler{
BaseURL: apimap[ApiService],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}
as.logHandler = &LogHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: getClientTransport(as.httpClient.Transport)},
Scheme: "http",
LogCache: []models.LogEntry{},
TheClock: clock.New(),
SyncInterval: 1 * time.Minute,
}

as.eventHandler = &EventHandler{
BaseURL: apimap[MongoDBDatastore],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}

as.projectHandler = &ProjectHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}

as.resourceHandler = &ResourceHandler{
BaseURL: apimap[ConfigurationService],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}
as.secretHandler = &SecretHandler{
BaseURL: apimap[SecretService],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}
as.sequenceControlHandler = &SequenceControlHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}
as.serviceHandler = &ServiceHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}
as.shipyardControlHandler = &ShipyardControllerHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: wrapOtelTransport(getClientTransport(as.httpClient.Transport))},
Scheme: "http",
}
as.stageHandler = &StageHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: otelhttp.NewTransport(as.httpClient.Transport)},
Scheme: "http",
}
as.uniformHandler = &UniformHandler{
BaseURL: apimap[ShipyardController],
HTTPClient: &http.Client{Transport: getClientTransport(as.httpClient.Transport)},
Scheme: "http",
}
return as, nil
}

// APIV1 retrieves the APIHandler
func (c *InternalAPISet) APIV1() APIV1Interface {
return c.apiHandler
}

// AuthV1 retrieves the AuthHandler
func (c *InternalAPISet) AuthV1() AuthV1Interface {
return c.authHandler
}

// EventsV1 retrieves the EventHandler
func (c *InternalAPISet) EventsV1() EventsV1Interface {
return c.eventHandler
}

// LogsV1 retrieves the LogHandler
func (c *InternalAPISet) LogsV1() LogsV1Interface {
return c.logHandler
}

// ProjectsV1 retrieves the ProjectHandler
func (c *InternalAPISet) ProjectsV1() ProjectsV1Interface {
return c.projectHandler
}

// ResourcesV1 retrieves the ResourceHandler
func (c *InternalAPISet) ResourcesV1() ResourcesV1Interface {
return c.resourceHandler
}

// SecretsV1 retrieves the SecretHandler
func (c *InternalAPISet) SecretsV1() SecretsV1Interface {
return c.secretHandler
}

// SequencesV1 retrieves the SequenceControlHandler
func (c *InternalAPISet) SequencesV1() SequencesV1Interface {
return c.sequenceControlHandler
}

// ServicesV1 retrieves the ServiceHandler
func (c *InternalAPISet) ServicesV1() ServicesV1Interface {
return c.serviceHandler
}

// StagesV1 retrieves the StageHandler
func (c *InternalAPISet) StagesV1() StagesV1Interface {
return c.stageHandler
}

// UniformV1 retrieves the UniformHandler
func (c *InternalAPISet) UniformV1() UniformV1Interface {
return c.uniformHandler
}

// ShipyardControlV1 retrieves the ShipyardControllerHandler
func (c *InternalAPISet) ShipyardControlV1() ShipyardControlV1Interface {
return c.shipyardControlHandler
}

// InternalAPIHandler is used instead of APIHandler from go-utils because we cannot support
// (unauthenticated) internal calls to the api-service at the moment. So this implementation
// will panic as soon as a client wants to call these methods
type InternalAPIHandler struct {
shipyardControllerApiHandler *APIHandler
}

func (i *InternalAPIHandler) SendEvent(event models.KeptnContextExtendedCE) (*models.EventContext, *models.Error) {
panic("SendEvent() is not not supported for internal usage")
}

func (i *InternalAPIHandler) TriggerEvaluation(project string, stage string, service string, evaluation models.Evaluation) (*models.EventContext, *models.Error) {
return i.shipyardControllerApiHandler.TriggerEvaluation(project, stage, service, evaluation)
}

func (i *InternalAPIHandler) CreateProject(project models.CreateProject) (string, *models.Error) {
return i.shipyardControllerApiHandler.CreateProject(project)
}

func (i *InternalAPIHandler) UpdateProject(project models.CreateProject) (string, *models.Error) {
return i.shipyardControllerApiHandler.UpdateProject(project)
}

func (i *InternalAPIHandler) DeleteProject(project models.Project) (*models.DeleteProjectResponse, *models.Error) {
return i.shipyardControllerApiHandler.DeleteProject(project)
}

func (i *InternalAPIHandler) CreateService(project string, service models.CreateService) (string, *models.Error) {
return i.shipyardControllerApiHandler.CreateService(project, service)
}

func (i *InternalAPIHandler) DeleteService(project string, service string) (*models.DeleteServiceResponse, *models.Error) {
return i.shipyardControllerApiHandler.DeleteService(project, service)
}

func (i *InternalAPIHandler) GetMetadata() (*models.Metadata, *models.Error) {
panic("GetMetadata() is not not supported for internal usage")
}
51 changes: 51 additions & 0 deletions pkg/api/utils/internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package api

import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)

func TestApiSetInternalMappings(t *testing.T) {
t.Run("TestInternalAPISet - Default API Mappings", func(t *testing.T) {
internal, err := NewInternal(nil)
require.Nil(t, err)
require.NotNil(t, internal)
assert.Equal(t, DefaultInClusterAPIMappings[MongoDBDatastore], internal.EventsV1().(*EventHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ApiService], internal.AuthV1().(*AuthHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.APIV1().(*InternalAPIHandler).shipyardControllerApiHandler.BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.ShipyardControlV1().(*ShipyardControllerHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.UniformV1().(*UniformHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.LogsV1().(*LogHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.SequencesV1().(*SequenceControlHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.StagesV1().(*StageHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[SecretService], internal.SecretsV1().(*SecretHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ConfigurationService], internal.ResourcesV1().(*ResourceHandler).BaseURL)
assert.Equal(t, DefaultInClusterAPIMappings[ShipyardController], internal.ProjectsV1().(*ProjectHandler).BaseURL)
})

t.Run("TestInternalAPISet - Override Mappings", func(t *testing.T) {
overrideMappings := InClusterAPIMappings{
ConfigurationService: "special-configuration-service:8080",
ShipyardController: "special-shipyard-controller:8080",
ApiService: "speclial-api-service:8080",
SecretService: "special-secret-service:8080",
MongoDBDatastore: "special-monogodb-datastore:8080",
}
internal, err := NewInternal(nil, overrideMappings)
require.Nil(t, err)
require.NotNil(t, internal)
assert.Equal(t, overrideMappings[MongoDBDatastore], internal.EventsV1().(*EventHandler).BaseURL)
assert.Equal(t, overrideMappings[ApiService], internal.AuthV1().(*AuthHandler).BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.APIV1().(*InternalAPIHandler).shipyardControllerApiHandler.BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.ShipyardControlV1().(*ShipyardControllerHandler).BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.UniformV1().(*UniformHandler).BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.LogsV1().(*LogHandler).BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.SequencesV1().(*SequenceControlHandler).BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.StagesV1().(*StageHandler).BaseURL)
assert.Equal(t, overrideMappings[SecretService], internal.SecretsV1().(*SecretHandler).BaseURL)
assert.Equal(t, overrideMappings[ConfigurationService], internal.ResourcesV1().(*ResourceHandler).BaseURL)
assert.Equal(t, overrideMappings[ShipyardController], internal.ProjectsV1().(*ProjectHandler).BaseURL)
})

}
Loading

0 comments on commit 3ed2fc6

Please sign in to comment.