Skip to content

Commit

Permalink
change package names and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jcollins-axway committed Feb 22, 2024
1 parent 2a93bc0 commit 69e9ff2
Show file tree
Hide file tree
Showing 12 changed files with 444 additions and 111 deletions.
111 changes: 74 additions & 37 deletions pkg/discovery/gateway/client.go → pkg/discovery/agent/agent.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package gateway
package agent

import (
"context"
"crypto/sha256"
"fmt"
"net/http"
"net/url"
"sync"

Expand All @@ -14,68 +13,106 @@ import (
"github.com/Axway/agent-sdk/pkg/apic"
"github.com/Axway/agent-sdk/pkg/apic/provisioning"
"github.com/Axway/agent-sdk/pkg/cache"
corecfg "github.com/Axway/agent-sdk/pkg/config"
"github.com/Axway/agent-sdk/pkg/filter"
"github.com/Axway/agent-sdk/pkg/util"
"github.com/Axway/agent-sdk/pkg/util/log"

"github.com/Axway/agents-kong/pkg/common"
"github.com/Axway/agents-kong/pkg/discovery/config"
"github.com/Axway/agents-kong/pkg/discovery/kong"
kutil "github.com/Axway/agents-kong/pkg/discovery/kong"
"github.com/Axway/agents-kong/pkg/discovery/subscription"
)

const (
https = "https"
)

var kongToCRDMapper = map[string]string{
kong.BasicAuthPlugin: provisioning.BasicAuthCRD,
kong.KeyAuthPlugin: provisioning.APIKeyCRD,
kong.OAuthPlugin: provisioning.OAuthSecretCRD,
}

func NewClient(agentConfig config.AgentConfig) (*Client, error) {
kongGatewayConfig := agentConfig.KongGatewayCfg
clientBase := &http.Client{}
kongClient, err := kutil.NewKongClient(clientBase, kongGatewayConfig)
type kongClient interface {
// Provisioning
CreateConsumer(ctx context.Context, id, name string) (*klib.Consumer, error)
AddConsumerACL(ctx context.Context, id string) error
DeleteConsumer(ctx context.Context, id string) error
// Credential
DeleteOauth2(ctx context.Context, consumerID, clientID string) error
DeleteHttpBasic(ctx context.Context, consumerID, username string) error
DeleteAuthKey(ctx context.Context, consumerID, authKey string) error
CreateHttpBasic(ctx context.Context, consumerID string, basicAuth *klib.BasicAuth) (*klib.BasicAuth, error)
CreateOauth2(ctx context.Context, consumerID string, oauth2 *klib.Oauth2Credential) (*klib.Oauth2Credential, error)
CreateAuthKey(ctx context.Context, consumerID string, keyAuth *klib.KeyAuth) (*klib.KeyAuth, error)
// Access Request
AddRouteACL(ctx context.Context, routeID, allowedID string) error
RemoveRouteACL(ctx context.Context, routeID, revokedID string) error
AddQuota(ctx context.Context, routeID, allowedID, quotaInterval string, quotaLimit int) error
// Discovery
ListServices(ctx context.Context) ([]*klib.Service, error)
ListRoutesForService(ctx context.Context, serviceId string) ([]*klib.Route, error)
GetSpecForService(ctx context.Context, service *klib.Service) ([]byte, error)
GetKongPlugins() *kong.Plugins
}

type Agent struct {
logger log.FieldLogger
centralCfg corecfg.CentralConfig
kongGatewayCfg *config.KongGatewayConfig
kongClient kongClient
plugins kong.Plugins
cache cache.Cache
filter filter.Filter
}

func NewAgent(agentConfig config.AgentConfig, agentOpts ...func(a *Agent)) (*Agent, error) {
ka := &Agent{
logger: log.NewFieldLogger().WithComponent("agent").WithPackage("kongAgent"),
centralCfg: agentConfig.CentralCfg,
kongGatewayCfg: agentConfig.KongGatewayCfg,
cache: cache.New(),
}
for _, o := range agentOpts {
o(ka)
}

var err error
if ka.kongClient == nil {
ka.kongClient, err = kong.NewKongClient(ka.kongGatewayCfg)
}
if err != nil {
return nil, err
}
daCache := cache.New()
logger := log.NewFieldLogger().WithField("component", "agent")

plugins, err := kongClient.Plugins.ListAll(context.Background())
pluginLister := ka.kongClient.GetKongPlugins()
if pluginLister == nil {
return nil, fmt.Errorf("could not get kong plugin lister")
}
plugins, err := ka.kongClient.GetKongPlugins().ListAll(context.Background())
if err != nil {
return nil, err
}

discoveryFilter, err := filter.NewFilter(agentConfig.KongGatewayCfg.Spec.Filter)
ka.filter, err = filter.NewFilter(agentConfig.KongGatewayCfg.Spec.Filter)
if err != nil {
return nil, err
}

if err = hasGlobalACLEnabledInPlugins(logger, plugins, agentConfig.KongGatewayCfg.ACL.Disable); err != nil {
logger.WithError(err).Error("ACL Plugin configured as required, but none found in Kong plugins.")
if err = hasGlobalACLEnabledInPlugins(ka.logger, plugins, agentConfig.KongGatewayCfg.ACL.Disable); err != nil {
ka.logger.WithError(err).Error("ACL Plugin configured as required, but none found in Kong plugins.")
return nil, err
}

provisionLogger := log.NewFieldLogger().WithComponent("provision").WithPackage("kong")
opts := []subscription.ProvisionerOption{}
if agentConfig.KongGatewayCfg.ACL.Disable {
opts = append(opts, subscription.WithACLDisable())
}
subscription.NewProvisioner(kongClient, provisionLogger, opts...)
subscription.NewProvisioner(ka.kongClient, opts...)
return ka, nil
}

return &Client{
logger: logger,
centralCfg: agentConfig.CentralCfg,
kongGatewayCfg: kongGatewayConfig,
kongClient: kongClient,
cache: daCache,
mode: common.Marketplace,
filter: discoveryFilter,
}, nil
func withKongClient(kongClient kongClient) func(a *Agent) {
return func(a *Agent) {
a.kongClient = kongClient
}
}

func pluginIsGlobal(p *klib.Plugin) bool {
Expand All @@ -99,13 +136,13 @@ func hasGlobalACLEnabledInPlugins(logger log.FieldLogger, plugins []*klib.Plugin
return fmt.Errorf("acl plugin is not enabled/installed, install and enable or change the config to disable this check")
}

func (gc *Client) DiscoverAPIs() error {
func (gc *Agent) DiscoverAPIs() error {
gc.logger.Info("execute discovery process")

ctx := context.Background()
var err error

plugins := kutil.Plugins{PluginLister: gc.kongClient.GetKongPlugins()}
plugins := kong.Plugins{PluginLister: gc.kongClient.GetKongPlugins()}
gc.plugins = plugins

services, err := gc.kongClient.ListServices(ctx)
Expand All @@ -118,7 +155,7 @@ func (gc *Client) DiscoverAPIs() error {
return nil
}

func (gc *Client) processKongServicesList(ctx context.Context, services []*klib.Service) {
func (gc *Agent) processKongServicesList(ctx context.Context, services []*klib.Service) {
wg := new(sync.WaitGroup)
for _, service := range services {
if !gc.filter.Evaluate(toTagsMap(service)) {
Expand Down Expand Up @@ -146,7 +183,7 @@ func toTagsMap(service *klib.Service) map[string]string {
return filters
}

func (gc *Client) processSingleKongService(ctx context.Context, service *klib.Service) error {
func (gc *Agent) processSingleKongService(ctx context.Context, service *klib.Service) error {
log := gc.logger.WithField(common.AttrServiceName, *service.Name)
log.Info("processing service")

Expand Down Expand Up @@ -184,7 +221,7 @@ func (gc *Client) processSingleKongService(ctx context.Context, service *klib.Se
return nil
}

func (gc *Client) specPreparation(ctx context.Context, route *klib.Route, service *klib.Service, spec apic.SpecProcessor) {
func (gc *Agent) specPreparation(ctx context.Context, route *klib.Route, service *klib.Service, spec apic.SpecProcessor) {
log := gc.logger.WithField(common.AttrRouteID, *route.ID).
WithField(common.AttrServiceID, *service.ID)

Expand Down Expand Up @@ -218,7 +255,7 @@ func (gc *Client) specPreparation(ctx context.Context, route *klib.Route, servic
log.Info("Successfully published to central")
}

func (gc *Client) processKongRoute(route *klib.Route) []apic.EndpointDefinition {
func (gc *Agent) processKongRoute(route *klib.Route) []apic.EndpointDefinition {
if route == nil {
return []apic.EndpointDefinition{}
}
Expand All @@ -234,7 +271,7 @@ func (gc *Client) processKongRoute(route *klib.Route) []apic.EndpointDefinition
return kRoute.GetEndpoints()
}

func (gc *Client) processKongAPI(
func (gc *Agent) processKongAPI(
ctx context.Context,
route *klib.Route,
service *klib.Service,
Expand Down Expand Up @@ -347,9 +384,9 @@ func (ka *KongAPI) oAuthSecurity(spec apic.OasSpecProcessor, config map[string]i

s := url.URL{}
for _, e := range ka.endpoints {
if e.Protocol == https {
if e.Protocol == httpsScheme {
s = url.URL{
Scheme: https,
Scheme: httpsScheme,
Host: fmt.Sprintf("%v:%v", e.Host, e.Port),
Path: e.BasePath,
}
Expand Down
142 changes: 142 additions & 0 deletions pkg/discovery/agent/agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package agent

import (
"context"
"testing"

"github.com/Axway/agent-sdk/pkg/agent"
"github.com/Axway/agent-sdk/pkg/apic/mock"
"github.com/Axway/agent-sdk/pkg/cache"
corecfg "github.com/Axway/agent-sdk/pkg/config"
"github.com/Axway/agent-sdk/pkg/filter"
"github.com/Axway/agent-sdk/pkg/util/log"
config "github.com/Axway/agents-kong/pkg/discovery/config"
"github.com/Axway/agents-kong/pkg/discovery/kong"
klib "github.com/kong/go-kong/kong"
"github.com/stretchr/testify/assert"
)

func stringPtr(s string) *string {
return &s
}

func boolPtr(b bool) *bool {
return &b
}

func intPtr(i int) *int {
return &i
}

func TestNewAgent(t *testing.T) {
testCases := map[string]struct {
gatewayConfig *config.KongGatewayConfig
client *mockKongClient
expectErr bool
}{
"error when plugin lister is not created": {
gatewayConfig: &config.KongGatewayConfig{},
client: &mockKongClient{},
expectErr: true,
},
"error getting kong plugins using lister": {
gatewayConfig: &config.KongGatewayConfig{},
client: &mockKongClient{
GetKongPluginsMock: func() *kong.Plugins {
return &kong.Plugins{PluginLister: &mockPluginLister{}}
},
},
expectErr: true,
},
"error hit because ACL was not installed": {
gatewayConfig: &config.KongGatewayConfig{},
client: &mockKongClient{
GetKongPluginsMock: func() *kong.Plugins {
return &kong.Plugins{PluginLister: &mockPluginLister{plugins: []*klib.Plugin{}}}
},
},
expectErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
cfg := config.AgentConfig{
CentralCfg: corecfg.NewCentralConfig(corecfg.DiscoveryAgent),
KongGatewayCfg: tc.gatewayConfig,
}
agent.InitializeForTest(&mock.Client{}, agent.TestWithMarketplace())

a, err := NewAgent(cfg, withKongClient(tc.client))
if tc.expectErr {
assert.Nil(t, a)
assert.NotNil(t, err)
}
})
}
}

func TestDiscovery(t *testing.T) {
testCases := map[string]struct {
client *mockKongClient
expectErr bool
}{
"expect error when services call fails": {
client: &mockKongClient{
GetKongPluginsMock: func() *kong.Plugins {
return &kong.Plugins{PluginLister: &mockPluginLister{plugins: []*klib.Plugin{}}}
},
},
expectErr: true,
},
"success when no services returned": {
client: &mockKongClient{
GetKongPluginsMock: func() *kong.Plugins {
return &kong.Plugins{PluginLister: &mockPluginLister{plugins: []*klib.Plugin{}}}
},
ListServicesMock: func(context.Context) ([]*klib.Service, error) {
return []*klib.Service{}, nil
},
},
},
"success when services returned but no routes": {
client: &mockKongClient{
GetKongPluginsMock: func() *kong.Plugins {
return &kong.Plugins{PluginLister: &mockPluginLister{plugins: []*klib.Plugin{}}}
},
ListServicesMock: func(context.Context) ([]*klib.Service, error) {
return []*klib.Service{
{
Enabled: boolPtr(true),
Host: stringPtr("petstore.com"),
ID: stringPtr("petstore-id"),
Name: stringPtr("PetStore"),
Tags: []*string{},
},
}, nil
},
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
f, _ := filter.NewFilter("")
ka := &Agent{
logger: log.NewFieldLogger().WithComponent("agent").WithPackage("kongAgent"),
centralCfg: corecfg.NewCentralConfig(corecfg.DiscoveryAgent),
kongGatewayCfg: &config.KongGatewayConfig{},
cache: cache.New(),
kongClient: tc.client,
filter: f,
}

// agent.InitializeForTest()

err := ka.DiscoverAPIs()
if tc.expectErr {
assert.NotNil(t, err)
return
}
assert.Nil(t, err)
})
}
}
26 changes: 26 additions & 0 deletions pkg/discovery/agent/definitions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package agent

import (
"github.com/Axway/agent-sdk/pkg/apic"
)

type KongAPI struct {
spec []byte
id string
name string
description string
version string
url string
documentation []byte
resourceType string
endpoints []apic.EndpointDefinition
image string
imageContentType string
crds []string
apiUpdateSeverity string
agentDetails map[string]string
tags []string
stage string
stageName string
ard string
}
Loading

0 comments on commit 69e9ff2

Please sign in to comment.