diff --git a/.ci/setup_kong.sh b/.ci/setup_kong.sh new file mode 100644 index 000000000..3f259f41d --- /dev/null +++ b/.ci/setup_kong.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e +# download Kong deb + +sudo apt-get update +sudo apt-get install openssl libpcre3 procps perl wget zlibc + +function setup_kong(){ + SWITCH="1.3.100" + + URL="https://kong.bintray.com/kong-deb/kong-${KONG_VERSION}.xenial.all.deb" + + if [[ "$KONG_VERSION" > "$SWITCH" ]]; + then + URL="https://kong.bintray.com/kong-deb/kong-${KONG_VERSION}.xenial.amd64.deb" + fi + + /usr/bin/curl -sL $URL -o kong.deb +} + +function setup_kong_enterprise(){ + KONG_VERSION="${KONG_VERSION#enterprise-}" + URL="https://kong.bintray.com/kong-enterprise-edition-deb/dists/kong-enterprise-edition-${KONG_VERSION}.xenial.all.deb" + RESPONSE_CODE=$(/usr/bin/curl -sL \ + -w "%{http_code}" \ + -u $KONG_ENTERPRISE_REPO_USERNAME:$KONG_ENTERPRISE_REPO_PASSSWORD \ + $URL -o kong.deb) + if [[ $RESPONSE_CODE != "200" ]]; then + echo "error retrieving kong enterprise package from ${URL}. response code ${RESPONSE_CODE}" + exit 1 + fi +} + +if [[ $KONG_VERSION == *"enterprise"* ]]; then + setup_kong_enterprise +else + setup_kong +fi + +sudo dpkg -i kong.deb +echo $KONG_LICENSE_DATA | sudo tee /etc/kong/license.json +export KONG_LICENSE_PATH=/tmp/license.json +export KONG_PASSWORD=kong +export KONG_ENFORCE_RBAC=on +export KONG_PORTAL=on + +sudo kong migrations bootstrap +sudo kong version +sudo kong start diff --git a/.github/workflows/integration-test-enterprise.yaml b/.github/workflows/integration-test-enterprise.yaml new file mode 100644 index 000000000..e616226b3 --- /dev/null +++ b/.github/workflows/integration-test-enterprise.yaml @@ -0,0 +1,52 @@ +name: "Integration Test : Enterprise" + +on: [push, pull_request] + +jobs: + test: + continue-on-error: true + strategy: + matrix: + kong_version: + - "enterprise-1.3.0.2" + - "enterprise-1.5.0.9" + - "enterprise-2.1.4.4" + - "enterprise-2.2.1.0" + - "enterprise-2.3.2.0" + env: + KONG_VERSION: ${{ matrix.kong_version }} + KONG_ENTERPRISE_REPO_USERNAME: ${{ secrets.KONG_ENTERPRISE_REPO_USERNAME }} + KONG_ENTERPRISE_REPO_PASSSWORD: ${{ secrets.KONG_ENTERPRISE_REPO_PASSSWORD }} + KONG_LICENSE_DATA: ${{ secrets.KONG_LICENSE_DATA }} + KONG_ANONYMOUS_REPORTS: "off" + KONG_ADMIN_TOKEN: kong + runs-on: ubuntu-latest + services: + postgres: + image: postgres:11.6-alpine + ports: + - 5432:5432 + # needed because the postgres container does not provide a healthcheck + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - name: Setup go + uses: actions/setup-go@v2 + with: + go-version: "^1.16" + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup Postgres + run: | + psql -c 'create database kong;' -U postgres -h 127.0.0.1 -p 5432 + psql -c 'create user kong;' -U postgres -h 127.0.0.1 -p 5432 + psql -c 'GRANT ALL PRIVILEGES ON DATABASE "kong" to kong;' -U postgres -h 127.0.0.1 -p 5432 + - name: Setup Kong + run: make setup-kong + - name: Run tests + run: make integration-test-coverage + - name: Upload Code Coverage + uses: codecov/codecov-action@v1 + with: + name: codecov-${{ matrix.kong_version }} + flags: ${{ matrix.kong_version }},integration,enterprise + fail_ci_if_error: true diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml new file mode 100644 index 000000000..49a8162af --- /dev/null +++ b/.github/workflows/integration-test.yaml @@ -0,0 +1,52 @@ +name: "Integration Test : Community" + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + kong_version: + - "1.0.3" + - "1.1.2" + - "1.2.0" + - "1.3.0" + - "1.4.0" + - "2.0.4" + - "2.1.0" + - "2.2.0" + - "2.3.0" + env: + KONG_VERSION: ${{ matrix.kong_version }} + KONG_LICENSE_DATA: ${{ secrets.KONG_LICENSE_DATA }} + KONG_ANONYMOUS_REPORTS: "off" + runs-on: ubuntu-latest + services: + postgres: + image: postgres:11.6-alpine + ports: + - 5432:5432 + # needed because the postgres container does not provide a healthcheck + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - name: Setup go + uses: actions/setup-go@v2 + with: + go-version: "^1.16" + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup Postgres + run: | + psql -c 'create database kong;' -U postgres -h 127.0.0.1 -p 5432 + psql -c 'create user kong;' -U postgres -h 127.0.0.1 -p 5432 + psql -c 'GRANT ALL PRIVILEGES ON DATABASE "kong" to kong;' -U postgres -h 127.0.0.1 -p 5432 + - name: Setup Kong + run: make setup-kong + - name: Run tests + run: make integration-test-coverage + - name: Upload Code Coverage + uses: codecov/codecov-action@v1 + with: + name: codecov-${{ matrix.kong_version }} + flags: ${{ matrix.kong_version }},integration,community + fail_ci_if_error: true diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4dc19cf82..0f4581e91 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,7 +9,7 @@ jobs: - name: Setup go uses: actions/setup-go@v2 with: - go-version: '^1.16' + go-version: "^1.16" - name: Checkout repository uses: actions/checkout@v2 - name: Setup golangci-lint @@ -19,7 +19,7 @@ jobs: - name: Verify Codegen run: make verify-codegen - name: Run tests with Coverage - run: make coverage + run: make test-coverage - name: Upload Code Coverage run: "bash <(curl -s https://codecov.io/bash)" - name: Build diff --git a/Makefile b/Makefile index cff781319..e65742103 100644 --- a/Makefile +++ b/Makefile @@ -25,9 +25,20 @@ update-codegen: go generate ./... ./scripts/update-deepcopy-gen.sh -.PHONY: coverage -coverage: +.PHONY: test-coverage +test-coverage: go test -race -v -count=1 -coverprofile=coverage.out.tmp ./... # ignoring generated code for coverage grep -E -v 'generated.deepcopy.go' coverage.out.tmp > coverage.out - rm -f coverage.out.tmp + rm -f coverage.out.tmp + +.PHONY: integration-test-coverage +integration-test-coverage: + go test -tags=integration -race -v -count=1 -coverprofile=coverage.out.tmp ./... + # ignoring generated code for coverage + grep -E -v 'generated.deepcopy.go' coverage.out.tmp > coverage.out + rm -f coverage.out.tmp + +.PHONY: setup-kong +setup-kong: + bash .ci/setup_kong.sh \ No newline at end of file diff --git a/cmd/common.go b/cmd/common.go index c32288b94..f7abb95d3 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -3,7 +3,6 @@ package cmd import ( "context" "fmt" - "net/http" "os" "github.com/blang/semver/v4" @@ -38,7 +37,7 @@ func SetStopCh(stopCh chan struct{}) { } // workspaceExists checks if workspace exists in Kong. -func workspaceExists(config utils.KongClientConfig) (bool, error) { +func workspaceExists(ctx context.Context, config utils.KongClientConfig) (bool, error) { if config.Workspace == "" { // default workspace always exists return true, nil @@ -54,7 +53,7 @@ func workspaceExists(config utils.KongClientConfig) (bool, error) { return false, err } - _, _, err = wsClient.Routes.List(context.TODO(), nil) + _, _, err = wsClient.Routes.List(ctx, nil) switch { case kong.IsNotFoundErr(err): return false, nil @@ -65,7 +64,7 @@ func workspaceExists(config utils.KongClientConfig) (bool, error) { } } -func syncMain(filenames []string, dry bool, parallelism, delay int, workspace string) error { +func syncMain(ctx context.Context, filenames []string, dry bool, parallelism, delay int, workspace string) error { // read target file targetContent, err := file.GetContentFromFiles(filenames) @@ -89,12 +88,12 @@ func syncMain(filenames []string, dry bool, parallelism, delay int, workspace st } // load Kong version after workspace - kongVersion, err := kongVersion(wsConfig) + kongVersion, err := kongVersion(ctx, wsConfig) if err != nil { return errors.Wrap(err, "reading Kong version") } - workspaceExists, err := workspaceExists(wsConfig) + workspaceExists, err := workspaceExists(ctx, wsConfig) if err != nil { return err } @@ -131,7 +130,7 @@ func syncMain(filenames []string, dry bool, parallelism, delay int, workspace st } if !dry { - _, err = rootClient.Workspaces.Create(nil, &kong.Workspace{Name: &wsConfig.Workspace}) + _, err = rootClient.Workspaces.Create(ctx, &kong.Workspace{Name: &wsConfig.Workspace}) if err != nil { return err } @@ -173,37 +172,32 @@ func syncMain(filenames []string, dry bool, parallelism, delay int, workspace st return nil } -func kongVersion(config utils.KongClientConfig) (semver.Version, error) { +func kongVersion(ctx context.Context, + config utils.KongClientConfig) (semver.Version, error) { var version string - workspace := config.Workspace - - // remove workspace to be able to call top-level / endpoint - config.Workspace = "" client, err := utils.GetKongClient(config) if err != nil { return semver.Version{}, err } - root, err := client.Root(nil) - if err != nil { - if workspace == "" { - return semver.Version{}, err - } - // try with workspace path - req, err := http.NewRequest("GET", - utils.CleanAddress(config.Address)+"/"+workspace+"/kong", - nil) + + if len(config.Workspace) > 0 { + req, err := client.NewRequest("GET", "/kong", nil, nil) if err != nil { return semver.Version{}, err } var resp map[string]interface{} - _, err = client.Do(nil, req, &resp) + _, err = client.Do(ctx, req, &resp) if err != nil { return semver.Version{}, err } version = resp["version"].(string) } else { + root, err := client.Root(ctx) + if err != nil { + return semver.Version{}, err + } version = root["version"].(string) } diff --git a/cmd/common_test.go b/cmd/common_test.go new file mode 100644 index 000000000..7954b0c1e --- /dev/null +++ b/cmd/common_test.go @@ -0,0 +1,71 @@ +// +build integration + +package cmd + +import ( + "context" + "github.com/blang/semver/v4" + "github.com/kong/deck/utils" + "github.com/kong/go-kong/kong" + "github.com/stretchr/testify/assert" + "os" + "strings" + "testing" +) + +var ( + defaultCtx = context.Background() +) + +func Test_kongVersion_Community(T *testing.T) { + kongVersionEnv, _ := os.LookupEnv("KONG_VERSION") + if strings.Contains(kongVersionEnv, "enterprise") { + T.Skip() + } + var expectedVersion = semver.MustParse(kongVersionEnv) + var config = NewTestClientConfig("") + version, err := kongVersion(defaultCtx, config) + assert := assert.New(T) + assert.Nil(err) + assert.NotNil(version) + assert.Equal(version.Major, expectedVersion.Major, "The two version should have the same major") + assert.Equal(version.Minor, expectedVersion.Minor, "The two version should have the same minor") +} + +func Test_kongVersion_Enterprise(T *testing.T) { + kongVersionEnv, _ := os.LookupEnv("KONG_VERSION") + if !strings.Contains(kongVersionEnv, "enterprise") { + T.Skip() + } + kongVersionEnv = strings.Replace(kongVersionEnv, "enterprise-", "", 1) + var expectedVersion = semver.MustParse(kongVersionEnv) + var config = NewTestClientConfig("") + version, err := kongVersion(defaultCtx, config) + assert := assert.New(T) + assert.Nil(err) + assert.NotNil(version) + assert.Equal(version.Major, expectedVersion.Major, "The two version should have the same major") + assert.Equal(version.Minor, expectedVersion.Minor, "The two version should have the same minor") + + client, err := utils.GetKongClient(config) + ws := &kong.Workspace{ + Name: kong.String("test"), + } + client.Workspaces.Create(defaultCtx, ws) + config = NewTestClientConfig(*ws.Name) + workspaceversion, err := kongVersion(defaultCtx, config) + assert.Nil(err) + assert.NotNil(workspaceversion) + assert.Equal(workspaceversion.Major, expectedVersion.Major, "The two version should have the same major") + assert.Equal(workspaceversion.Minor, expectedVersion.Minor, "The two version should have the same minor") + +} + +func NewTestClientConfig(workspace string) utils.KongClientConfig { + kongAdminToken, _ := os.LookupEnv("KONG_ADMIN_TOKEN") + return utils.KongClientConfig{ + Address: "http://localhost:8001", + Workspace: workspace, + Headers: []string{"kong-admin-token:" + kongAdminToken}, + } +} diff --git a/cmd/diff.go b/cmd/diff.go index 9f39d3966..03d53ecc1 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -24,7 +24,7 @@ that will be created or updated or deleted. `, Args: validateNoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return syncMain(diffCmdKongStateFile, true, diffCmdParallelism, 0, diffWorkspace) + return syncMain(cmd.Context(), diffCmdKongStateFile, true, diffCmdParallelism, 0, diffWorkspace) }, PreRunE: func(cmd *cobra.Command, args []string) error { if len(diffCmdKongStateFile) == 0 { diff --git a/cmd/dump.go b/cmd/dump.go index 459a61d06..09580e37e 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "net/http" "strings" @@ -21,7 +22,7 @@ var ( dumpWithID bool ) -func listWorkspaces(client *kong.Client, baseURL string) ([]string, error) { +func listWorkspaces(ctx context.Context, client *kong.Client, baseURL string) ([]string, error) { type Workspace struct { Name string } @@ -35,7 +36,7 @@ func listWorkspaces(client *kong.Client, baseURL string) ([]string, error) { if err != nil { return nil, errors.Wrap(err, "building request for fetching workspaces") } - _, err = client.Do(nil, req, &response) + _, err = client.Do(ctx, req, &response) if err != nil { return nil, errors.Wrap(err, "fetching workspaces from Kong") } @@ -58,7 +59,7 @@ The file can then be read using the Sync o Diff command to again configure Kong.`, Args: validateNoArgs, RunE: func(cmd *cobra.Command, args []string) error { - + var ctx = cmd.Context() wsClient, err := utils.GetKongClient(rootConfig) if err != nil { return err @@ -74,7 +75,7 @@ configure Kong.`, if dumpCmdKongStateFile != "kong" { return errors.New("output-file cannot be specified with --all-workspace flag") } - workspaces, err := listWorkspaces(wsClient, rootConfig.Address) + workspaces, err := listWorkspaces(ctx, wsClient, rootConfig.Address) if err != nil { return err } @@ -118,7 +119,7 @@ configure Kong.`, if dumpWorkspace != "" { wsConfig := rootConfig.ForWorkspace(dumpWorkspace) - exists, err := workspaceExists(wsConfig) + exists, err := workspaceExists(ctx, wsConfig) if err != nil { return err } diff --git a/cmd/ping.go b/cmd/ping.go index 648f721c3..e5cedc88e 100644 --- a/cmd/ping.go +++ b/cmd/ping.go @@ -21,7 +21,7 @@ can connect to Kong's Admin API or not.`, RunE: func(cmd *cobra.Command, args []string) error { wsConfig := rootConfig.ForWorkspace(pingWorkspace) - version, err := kongVersion(wsConfig) + version, err := kongVersion(cmd.Context(), wsConfig) if err != nil { return errors.Wrap(err, "reading Kong version") } diff --git a/cmd/reset.go b/cmd/reset.go index b575cd93f..0beda45a7 100644 --- a/cmd/reset.go +++ b/cmd/reset.go @@ -57,17 +57,17 @@ By default, this command will ask for a confirmation prompt.`, if resetAllWorkspaces && resetWorkspace != "" { return errors.New("workspace cannot be specified with --all-workspace flag") } - + var ctx = cmd.Context() // Kong Enterprise var workspaces []string if resetAllWorkspaces { - workspaces, err = listWorkspaces(rootClient, rootConfig.Address) + workspaces, err = listWorkspaces(ctx, rootClient, rootConfig.Address) if err != nil { return err } } if resetWorkspace != "" { - exists, err := workspaceExists(rootConfig.ForWorkspace(resetWorkspace)) + exists, err := workspaceExists(ctx, rootConfig.ForWorkspace(resetWorkspace)) if err != nil { return err } diff --git a/cmd/sync.go b/cmd/sync.go index b121e3610..509915192 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -21,7 +21,8 @@ var syncCmd = &cobra.Command{ to get Kong's state in sync with the input state.`, Args: validateNoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return syncMain(syncCmdKongStateFile, false, syncCmdParallelism, syncCmdDBUpdateDelay, syncWorkspace) + return syncMain(cmd.Context(), syncCmdKongStateFile, false, syncCmdParallelism, + syncCmdDBUpdateDelay, syncWorkspace) }, PreRunE: func(cmd *cobra.Command, args []string) error { if len(syncCmdKongStateFile) == 0 {