Skip to content

Commit

Permalink
feat: add /status/ready function
Browse files Browse the repository at this point in the history
Signed-off-by: Jintao Zhang <[email protected]>
  • Loading branch information
tao12345666333 committed Jul 20, 2024
1 parent afe0c7b commit f154725
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .ci/setup_kong.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function deploy_kong_postgres()
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-e "KONG_STATUS_LISTEN=0.0.0.0:8100" \
-e "KONG_ADMIN_GUI_AUTH=basic-auth" \
-e "KONG_ADMIN_GUI_SESSION_CONF={}" \
-e "KONG_ENFORCE_RBAC=on" \
Expand All @@ -44,6 +45,7 @@ function deploy_kong_postgres()
-p 8443:8443 \
-p 127.0.0.1:8001:8001 \
-p 127.0.0.1:8444:8444 \
-p 127.0.0.1:8100:8100 \
--label "$DOCKER_LABEL" \
$KONG_IMAGE
waitContainer "Kong" 8001 0.2
Expand All @@ -59,6 +61,7 @@ function deploy_kong_dbless()
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-e "KONG_STATUS_LISTEN=0.0.0.0:8100" \
-e "KONG_ADMIN_GUI_AUTH=basic-auth" \
-e "KONG_ENFORCE_RBAC=on" \
-e "KONG_PORTAL=on" \
Expand All @@ -70,6 +73,7 @@ function deploy_kong_dbless()
-p 8443:8443 \
-p 127.0.0.1:8001:8001 \
-p 127.0.0.1:8444:8444 \
-p 127.0.0.1:8100:8100 \
--label "$DOCKER_LABEL" \
$KONG_IMAGE
waitContainer "Kong" 8001 0.2
Expand Down
2 changes: 2 additions & 0 deletions .ci/setup_kong_ee.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function deploy_kong_ee()
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_STATUS_LISTEN=0.0.0.0:8100" \
-e "KONG_PORTAL_GUI_URI=127.0.0.1:8003" \
-e "KONG_ADMIN_GUI_URL=http://127.0.0.1:8002" \
-e "KONG_LICENSE_DATA=$KONG_LICENSE_DATA" \
Expand All @@ -59,6 +60,7 @@ function deploy_kong_ee()
-p 8445:8445 \
-p 8003:8003 \
-p 8004:8004 \
-p 127.0.0.1:8100:8100 \
--label "$DOCKER_LABEL" \
$KONG_IMAGE
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ replace github.com/imdario/mergo v0.3.12 => github.com/Kong/mergo v0.3.13
retract v0.39.1

require (
github.com/Masterminds/semver v1.5.0
github.com/google/go-cmp v0.6.0
github.com/google/go-querystring v1.1.0
github.com/google/uuid v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/Kong/mergo v0.3.13 h1:J+RyBootTG0GSmmzPBF4GqhHDLBKuSZeuaIyAHtOF9Y=
github.com/Kong/mergo v0.3.13/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
91 changes: 85 additions & 6 deletions kong/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,23 @@ import (
"net/http/httputil"
"net/url"
"os"
"regexp"
"strings"
"sync"
"time"

"github.com/kong/go-kong/kong/custom"
)

const (
// ref: https://docs.konghq.com/gateway/latest/production/networking/default-ports/
// defaultBaseURL is the endpoint for admin API
defaultBaseURL = "http://localhost:8001"
// defaultStatusURL is the endpoint for status API
// By default, the Status API listens on 127.0.0.1
// If you need to request it from elsewhere,
// please modify the `KONG_STATUS_LISTEN` environment variable of Gateway.
defaultStatusURL = "http://localhost:8007"
// DefaultTimeout is the timeout used for network connections and requests
// including TCP, TLS and HTTP layers.
DefaultTimeout = 60 * time.Second
Expand All @@ -40,6 +49,7 @@ var defaultCtx = context.Background()
type Client struct {
client *http.Client
baseRootURL string
statusURL string
workspace string // Do not access directly. Use Workspace()/SetWorkspace().
UserAgent string // User-Agent for the client.
workspaceLock sync.RWMutex // Synchronizes access to workspace.
Expand Down Expand Up @@ -111,8 +121,39 @@ type Status struct {
ConfigurationHash string `json:"configuration_hash,omitempty" yaml:"configuration_hash,omitempty"`
}

// NewClient returns a Client which talks to Admin API of Kong
func NewClient(baseURL *string, client *http.Client) (*Client, error) {
type StatusMessage struct {
Message string `json:"message"`
}

type RequestOptions struct {
BaseURL *string
StatusURL *string
}

func parseStatusListen(listen string) string {
re := regexp.MustCompile(`^([\w\.:]+)\s*(.*)?`)
matches := re.FindStringSubmatch(listen)

if len(matches) == 0 {
return ""
}

address := matches[1]
extraParams := matches[2]

// use http protocol by default
protocol := "http://"

// if the listen address contains ssl, use https protocol
if strings.Contains(extraParams, "ssl") {
protocol = "https://"
}

return fmt.Sprintf("%s%s", protocol, address)
}

// NewClientWithOpts returns a Client which talks to Kong's Admin API and Status API.
func NewClientWithOpts(requestOpts RequestOptions, client *http.Client) (*Client, error) {
if client == nil {
transport := &http.Transport{
DialContext: (&net.Dialer{
Expand All @@ -128,18 +169,36 @@ func NewClient(baseURL *string, client *http.Client) (*Client, error) {
kong := new(Client)
kong.client = client
var rootURL string
if baseURL != nil {
rootURL = *baseURL
if requestOpts.BaseURL != nil {
rootURL = *requestOpts.BaseURL
} else if urlFromEnv := os.Getenv("KONG_ADMIN_URL"); urlFromEnv != "" {
rootURL = urlFromEnv
} else {
rootURL = defaultBaseURL
}
url, err := url.ParseRequestURI(rootURL)
parsedRootURL, err := url.ParseRequestURI(rootURL)
if err != nil {
return nil, fmt.Errorf("parsing URL: %w", err)
}
kong.baseRootURL = url.String()
kong.baseRootURL = parsedRootURL.String()

var statusURL string
if requestOpts.StatusURL != nil {
statusURL = *requestOpts.StatusURL
} else if listenFromEnv := os.Getenv("KONG_STATUS_LISTEN"); listenFromEnv != "" {
// KONG_STATUS_LISTEN supports the configuration formats of Kong/Nginx.
// Only the most commonly used format is handled here.
// TODO: Support more formats.
// https://github.com/Kong/kong/blob/2384d2e129d223010fb8a4bb686afb028dca972f/kong.conf.default#L643-L663
statusURL = parseStatusListen(listenFromEnv)
} else {
statusURL = defaultStatusURL
}
parsedStatusURL, err := url.ParseRequestURI(statusURL)
if err != nil {
return nil, fmt.Errorf("parsing statusURL: %w", err)
}
kong.statusURL = parsedStatusURL.String()

kong.common.client = kong
kong.ConsumerGroupConsumers = (*ConsumerGroupConsumerService)(&kong.common)
Expand Down Expand Up @@ -199,6 +258,11 @@ func NewClient(baseURL *string, client *http.Client) (*Client, error) {
return kong, nil
}

// NewClient returns a Client which talks to Admin API of Kong
func NewClient(baseURL *string, client *http.Client) (*Client, error) {
return NewClientWithOpts(RequestOptions{BaseURL: baseURL}, client)
}

// SetDoer sets a Doer implementation to be used for custom request dispatching.
func (c *Client) SetDoer(doer Doer) *Client {
c.doer = doer
Expand Down Expand Up @@ -391,6 +455,21 @@ func (c *Client) Status(ctx context.Context) (*Status, error) {
return &s, nil
}

// Ready returns 200 only after the Kong node has configured itself and is ready to start proxying traffic.
func (c *Client) Ready(ctx context.Context) (*StatusMessage, error) {
req, err := http.NewRequest("GET", c.statusURL+"/status/ready", nil)
if err != nil {
return nil, err
}

var sm StatusMessage
_, err = c.Do(ctx, req, &sm)
if err != nil {
return nil, err
}
return &sm, nil
}

// Config gets the specified config from the configured Admin API endpoint
// and should contain the JSON serialized body that adheres to the configuration
// format specified at:
Expand Down
48 changes: 48 additions & 0 deletions kong/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"testing"

"github.com/Masterminds/semver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
Expand All @@ -34,6 +35,53 @@ func TestKongStatus(T *testing.T) {
assert.NotNil(status)
}

func TestKongReady(t *testing.T) {
kongImageTag := os.Getenv("KONG_IMAGE_TAG")
if kongImageTag == "" {
t.Skip("KONG_IMAGE_TAG environment variable is not set")
}

currentVersion, err := semver.NewVersion(kongImageTag)
if err != nil {
// We have set the KONG_IMAGE_TAG env var to master when running the test for nightly builds.
if kongImageTag != "master" {
t.Fatalf("Failed to parse KONG_IMAGE_TAG: %v", err)
}
} else {

// This API was only made available since Kong v3.3.
// ref: https://docs.konghq.com/gateway/api/status/latest/#/default/get_status_ready
minVersion, err := semver.NewVersion("3.3")
if err != nil {
t.Fatalf("Failed to parse the minimum required version: %v", err)
}

// Compare the versions and skip the test if the current version is less than the minimum version
if currentVersion.LessThan(minVersion) {
t.Skipf("Skipping test because KONG_IMAGE_TAG %s is less than 3.3", kongImageTag)
}
}

assert := assert.New(t)

client, err := NewTestClientWithOpts(RequestOptions{
BaseURL: String("http://localhost:8001"),
StatusURL: String("http://localhost:8100"),
}, nil)
assert.NoError(err)
assert.NotNil(client)

sm, err := client.Ready(defaultCtx)
if err != nil {
// for dbless mode, the ready endpoint returns 503
assert.Equal("HTTP status 503 (message: \"no configuration available (empty configuration present)\")", err.Error())
assert.Nil(sm)
} else {
// for db-mode, the ready endpoint returns 200
assert.Equal("ready", sm.Message)
}
}

func TestKongConfig(t *testing.T) {
RunWhenDBMode(t, "off")
client, err := NewTestClient(nil, nil)
Expand Down
10 changes: 10 additions & 0 deletions kong/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ func SkipWhenEnterprise(t *testing.T) {
}
}

func NewTestClientWithOpts(opts RequestOptions, client *http.Client) (*Client, error) {
return NewClientWithOpts(
RequestOptions{
BaseURL: opts.BaseURL,
StatusURL: opts.StatusURL,
},
client,
)
}

func NewTestClient(baseURL *string, client *http.Client) (*Client, error) {
if value, exists := os.LookupEnv("KONG_ADMIN_TOKEN"); exists && value != "" {
c := &http.Client{}
Expand Down

0 comments on commit f154725

Please sign in to comment.