Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APIGOV-26621 - Get spec from dev portal #36

Merged
merged 7 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions pkg/cmd/discovery/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ var DiscoveryCmd corecmd.AgentRootCmd
var agentConfig config.AgentConfig

const (
cfgKongAdminURL = "kong.admin.url"
cfgKongAdminAPIKey = "kong.admin.auth.apikey.value"
cfgKongAdminAPIKeyHeader = "kong.admin.auth.apikey.header"
cfgKongProxyHost = "kong.proxy.host"
cfgKongProxyPortHttp = "kong.proxy.port.http"
cfgKongProxyPortHttps = "kong.proxy.port.https"
cfgKongSpecURLPaths = "kong.spec.urlPaths"
cfgKongSpecLocalPath = "kong.spec.localPath"
cfgKongAdminURL = "kong.admin.url"
cfgKongAdminAPIKey = "kong.admin.auth.apikey.value"
cfgKongAdminAPIKeyHeader = "kong.admin.auth.apikey.header"
cfgKongProxyHost = "kong.proxy.host"
cfgKongProxyPortHttp = "kong.proxy.port.http"
cfgKongProxyPortHttps = "kong.proxy.port.https"
cfgKongSpecURLPaths = "kong.spec.urlPaths"
cfgKongSpecLocalPath = "kong.spec.localPath"
cfgKongSpecDevPortalEnabled = "kong.spec.devPortalEnabled"
jcollins-axway marked this conversation as resolved.
Show resolved Hide resolved
)

func init() {
Expand All @@ -45,6 +46,7 @@ func init() {
rootProps.AddIntProperty(cfgKongProxyPortHttps, 443, "The Kong proxy https port")
rootProps.AddStringSliceProperty(cfgKongSpecURLPaths, []string{}, "URL paths that the agent will look in for spec files")
rootProps.AddStringProperty(cfgKongSpecLocalPath, "", "Local paths where the agent will look for spec files")
rootProps.AddBoolProperty(cfgKongSpecDevPortalEnabled, false, "Dev Portal is used to download spec files")
}

// Callback that agent will call to process the execution
Expand Down Expand Up @@ -99,8 +101,9 @@ func initConfig(centralConfig corecfg.CentralConfig) (interface{}, error) {
},
},
Spec: config.KongSpecConfig{
URLPaths: rootProps.StringSlicePropertyValue(cfgKongSpecURLPaths),
LocalPath: rootProps.StringPropertyValue(cfgKongSpecLocalPath),
URLPaths: rootProps.StringSlicePropertyValue(cfgKongSpecURLPaths),
LocalPath: rootProps.StringPropertyValue(cfgKongSpecLocalPath),
DevPortalEnabled: rootProps.BoolPropertyValue(cfgKongSpecDevPortalEnabled),
},
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/config/discovery/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ type KongProxyPortConfig struct {
}

type KongSpecConfig struct {
URLPaths []string `config:"urlPaths"`
LocalPath string `config:"localPaths"`
URLPaths []string `config:"urlPaths"`
LocalPath string `config:"localPath"`
DevPortalEnabled bool `config:"devPortalEnabled"`
}

// KongGatewayConfig - represents the config for gateway
Expand Down
72 changes: 69 additions & 3 deletions pkg/kong/kongclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kong
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -44,13 +45,22 @@ type KongAPIClient interface {
GetKongPlugins() *Plugins
}

type KongServiceSpec struct {
Contents string `json:"contents"`
CreatedAt int `json:"created_at"`
ID string `json:"id"`
Path string `json:"path"`
Checksum string `json:"checksum"`
}

type KongClient struct {
*klib.Client
logger log.FieldLogger
baseClient DoRequest
kongAdminEndpoint string
specURLPaths []string
specLocalPath string
devPortalEnabled bool
clientTimeout time.Duration
}

Expand Down Expand Up @@ -80,6 +90,7 @@ func NewKongClient(baseClient *http.Client, kongConfig *config.KongGatewayConfig
kongAdminEndpoint: kongConfig.Admin.URL,
specURLPaths: kongConfig.Spec.URLPaths,
specLocalPath: kongConfig.Spec.LocalPath,
devPortalEnabled: kongConfig.Spec.DevPortalEnabled,
clientTimeout: 60 * time.Second,
}, nil
}
Expand All @@ -100,6 +111,10 @@ func (k KongClient) GetSpecForService(ctx context.Context, service *klib.Service
return k.getSpecFromLocal(ctx, service)
}

if k.devPortalEnabled {
return k.getSpecFromDevPortal(ctx, *service.ID)
}

// all three fields are needed to form the backend URL used in discovery process
if service.Protocol == nil && service.Host == nil {
err := fmt.Errorf("fields for backend URL are not set")
Expand All @@ -116,6 +131,7 @@ func (k KongClient) GetSpecForService(ctx context.Context, service *klib.Service

func (k KongClient) getSpecFromLocal(ctx context.Context, service *klib.Service) ([]byte, error) {
log := k.logger.WithField("serviceID", service.ID).WithField("serviceName", service.Name)
log.Info("getting spec from local storage")

specTag := ""
for _, tag := range service.Tags {
Expand Down Expand Up @@ -157,7 +173,44 @@ func (k KongClient) loadSpecFile(specFilePath string) ([]byte, error) {
return data, nil
}

func (k KongClient) getSpecFromDevPortal(ctx context.Context, serviceID string) ([]byte, error) {
log := k.logger.WithField("serviceID", serviceID)
log.Info("getting spec file from dev portal")

endpoint := fmt.Sprintf("%s/services/%s/document_objects", k.kongAdminEndpoint, serviceID)
req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
if err != nil {
log.WithError(err).Error("failed to create request")
return nil, err
}
res, err := k.baseClient.Do(req)
if err != nil {
log.WithError(err).Error("failed to execute request")
return nil, err
}
data, err := io.ReadAll(res.Body)
if err != nil {
log.WithError(err).Error("failed to read body")
return nil, err
}
documents := &DocumentObjects{}
err = json.Unmarshal(data, documents)
if err != nil {
log.WithError(err).Error("failed to unmarshal")
return nil, err
}
if len(documents.Data) < 1 {
log.Debug("no documents found")
return nil, nil
}

endpoint = fmt.Sprintf("%s/default/files/%s", k.kongAdminEndpoint, documents.Data[0].Path)
return k.getSpec(ctx, endpoint, true)
}

func (k KongClient) getSpecFromBackend(ctx context.Context, backendURL string) ([]byte, error) {
k.logger.Info("trying to get spec file from service backend")

if len(k.specURLPaths) == 0 {
k.logger.Info("no spec paths configured")
return nil, nil
Expand All @@ -166,7 +219,7 @@ func (k KongClient) getSpecFromBackend(ctx context.Context, backendURL string) (
for _, specPath := range k.specURLPaths {
endpoint := fmt.Sprintf("%s/%s", backendURL, strings.TrimPrefix(specPath, "/"))

spec, err := k.getSpec(ctx, endpoint)
spec, err := k.getSpec(ctx, endpoint, false)
if err != nil {
return nil, err
}
Expand All @@ -180,7 +233,7 @@ func (k KongClient) getSpecFromBackend(ctx context.Context, backendURL string) (
return nil, nil
}

func (k KongClient) getSpec(ctx context.Context, endpoint string) ([]byte, error) {
func (k KongClient) getSpec(ctx context.Context, endpoint string, fromDevPortal bool) ([]byte, error) {
ctxTimeout, cancel := context.WithTimeout(ctx, k.clientTimeout)
defer cancel()

Expand All @@ -198,12 +251,25 @@ func (k KongClient) getSpec(ctx context.Context, endpoint string) ([]byte, error
return nil, nil
}

specContent, err := io.ReadAll(res.Body)
data, err := io.ReadAll(res.Body)
if err != nil {
k.logger.WithError(err).Error("failed to read body")
return nil, err
}

var specContent []byte
if fromDevPortal {
kongServiceSpec := &KongServiceSpec{}
err = json.Unmarshal(data, kongServiceSpec)
if err != nil {
k.logger.WithError(err).Error("failed to unmarshal")
return nil, err
}
specContent = []byte(kongServiceSpec.Contents)
} else {
specContent = data
}

specParser := apic.NewSpecResourceParser(specContent, "")
err = specParser.Parse()
if err != nil {
Expand Down