Add CosmosDB state store conformance tests (#622)

* Add CosmosDB conformance tests

* Fix workflow & lint

* Add more disclaimers to README.md
This commit is contained in:
Nghia Tran 2021-01-20 14:38:09 -08:00 committed by GitHub
parent 079d0b1bd0
commit 3086450ced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 12 deletions

View File

@ -23,16 +23,33 @@ jobs:
# #
# The KeyVault policy must be granted to your Service Principal using # The KeyVault policy must be granted to your Service Principal using
# az keyvault set-policy -n $AZURE_KEYVAULT --secret-permissions get list --spn $SPN_CLIENT_ID # az keyvault set-policy -n $AZURE_KEYVAULT --secret-permissions get list --spn $SPN_CLIENT_ID
AZURE_KEYVAULT: conformance-tests-secret AZURE_KEYVAULT: dapr-conf-tests
strategy: strategy:
fail-fast: false # Keep running even if one component fails fail-fast: false # Keep running even if one component fails
matrix: matrix:
# List here all the components that needs to be invoked in this workflow. # List here all the components that needs to be invoked in this workflow.
component: component:
- state.redis
- pubsub.redis - pubsub.redis
- secretstores.localenv - secretstores.localenv
- state.cosmosdb
- state.redis
include:
- component: state.cosmosdb
# Unfortunately, Azure secrets can't have underscores in
# names, while environment variables with hyphens ('-') are
# troublesome.
#
# We work around here by leveraging the fact that
# environment variable names are case sensitive, so
# CamelCase would still work.
#
# That is slightly better than something like
# AZURECOSMOSDBMASTERKEY, which is extremely hard to read
# and errorprone.
#
# Only list the secrets you need for this component.
required-secrets: AzureCosmosDBMasterKey,AzureCosmosDBUrl,AzureCosmosDB,AzureCosmosDBCollection
steps: steps:
- name: Check out code onto GOPATH - name: Check out code onto GOPATH
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -65,11 +82,11 @@ jobs:
NAME=$(echo ${{ matrix.component }} | cut -d. -f2) NAME=$(echo ${{ matrix.component }} | cut -d. -f2)
KIND_UPPER="$(tr '[:lower:]' '[:upper:]' <<< ${KIND:0:1})${KIND:1}" KIND_UPPER="$(tr '[:lower:]' '[:upper:]' <<< ${KIND:0:1})${KIND:1}"
if [ ${KIND}=="secretstores" ]; then if [ "${KIND}" = "secretstores" ]; then
KIND_UPPER=SecretStore KIND_UPPER=SecretStore
fi fi
if [ ${KIND}=="output-binding" ]; then if [ "${KIND}" = "output-binding" ]; then
KIND_UPPER=OutputBinding KIND_UPPER=OutputBinding
fi fi

1
go.mod
View File

@ -71,6 +71,7 @@ require (
gopkg.in/couchbase/gocb.v1 v1.6.4 gopkg.in/couchbase/gocb.v1 v1.6.4
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.20.0 k8s.io/api v0.20.0
k8s.io/apiextensions-apiserver v0.17.2
k8s.io/apimachinery v0.20.0 k8s.io/apimachinery v0.20.0
k8s.io/client-go v0.20.0 k8s.io/client-go v0.20.0
) )

View File

@ -0,0 +1,16 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.azure.cosmosdb
version: v1
metadata:
- name: url
value: ${{AzureCosmosDBUrl}}
- name: masterKey
value: ${{AzureCosmosDBMasterKey}}
- name: database
value: ${{AzureCosmosDB}}
- name: collection
value: ${{AzureCosmosDBCollection}}

View File

@ -4,4 +4,13 @@ components:
allOperations: true allOperations: true
config: config:
maxInitDuration: 30 maxInitDuration: 30
maxSetDuration: 20 maxSetDuration: 20
- component: cosmosdb
allOperations: true
config:
maxInitDuration: 900
maxSetDuration: 300
maxGetDuration: 300
maxBulkSetDuration: 300
maxBulkDeleteDuration: 300
maxDeleteDuration: 300

View File

@ -6,13 +6,13 @@
2. All the conformance tests are within the `tests/conformance` directory. 2. All the conformance tests are within the `tests/conformance` directory.
3. All the configurations are in the `tests/config` directory. 3. All the configurations are in the `tests/config` directory.
4. Each of the component specific `component` definition are in their specific `component type` folder in the `tests/config` folder. For eg: `redis` statestore component definition within `state` directory. And the other component types are `state`, `secretstores`, `pubsub` and `bindings`. 4. Each of the component specific `component` definition are in their specific `component type` folder in the `tests/config` folder. For eg: `redis` statestore component definition within `state` directory. And the other component types are `state`, `secretstores`, `pubsub` and `bindings`.
5. Similar to the component definitions, each component type has its own set of the conformance tests definitions. 5. Similar to the component definitions, each component type has its own set of the conformance tests definitions.
6. Each component type contains a `tests.yml` definition that defines the component to be tested along with component specific test configuration. 6. Each component type contains a `tests.yml` definition that defines the component to be tested along with component specific test configuration.
7. All the tests configurations are defined in `common.go` file. 7. All the tests configurations are defined in `common.go` file.
8. Each component type has its own `_test` file to trigger the conformance tests. 8. Each component type has its own `_test` file to trigger the conformance tests.
## Running conformance tests ## Running conformance tests
1. Test test setup is independent of the test run. 1. Test test setup is independent of the test run.
2. Run Redis with 6379 exposed locally. 2. Run Redis with 6379 exposed locally.
3. Run `make conf-tests` to run the conformance tests locally. 3. Run `make conf-tests` to run the conformance tests locally.
> Note: Any component that cannot run as dockerized containers are not tested locally. > Note Some conformance tests require credentials in the form of environment variables. For examples Azure CosmosDB conformance tests will need to have Azure CosmosDB credentials. You will need to supply them to make these tests pass.

View File

@ -1,3 +1,8 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------
package conformance package conformance
import ( import (
@ -5,6 +10,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@ -12,11 +18,13 @@ import (
"fortio.org/fortio/log" "fortio.org/fortio/log"
"github.com/dapr/components-contrib/pubsub" "github.com/dapr/components-contrib/pubsub"
p_servicebus "github.com/dapr/components-contrib/pubsub/azure/servicebus"
p_redis "github.com/dapr/components-contrib/pubsub/redis" p_redis "github.com/dapr/components-contrib/pubsub/redis"
"github.com/dapr/components-contrib/secretstores" "github.com/dapr/components-contrib/secretstores"
ss_local_env "github.com/dapr/components-contrib/secretstores/local/env" ss_local_env "github.com/dapr/components-contrib/secretstores/local/env"
ss_local_file "github.com/dapr/components-contrib/secretstores/local/file" ss_local_file "github.com/dapr/components-contrib/secretstores/local/file"
"github.com/dapr/components-contrib/state" "github.com/dapr/components-contrib/state"
s_cosmosdb "github.com/dapr/components-contrib/state/azure/cosmosdb"
s_redis "github.com/dapr/components-contrib/state/redis" s_redis "github.com/dapr/components-contrib/state/redis"
conf_pubsub "github.com/dapr/components-contrib/tests/conformance/pubsub" conf_pubsub "github.com/dapr/components-contrib/tests/conformance/pubsub"
conf_secret "github.com/dapr/components-contrib/tests/conformance/secretstores" conf_secret "github.com/dapr/components-contrib/tests/conformance/secretstores"
@ -74,10 +82,27 @@ func LoadComponents(componentPath string) ([]v1alpha1.Component, error) {
return components, nil return components, nil
} }
func LookUpEnv(key string) string {
if val, ok := os.LookupEnv(key); ok {
return val
}
return ""
}
func ConvertMetadataToProperties(items []v1alpha1.MetadataItem) map[string]string { func ConvertMetadataToProperties(items []v1alpha1.MetadataItem) map[string]string {
properties := map[string]string{} properties := map[string]string{}
for _, c := range items { for _, c := range items {
properties[c.Name] = c.Value.String() val := c.Value.String()
if strings.HasPrefix(c.Value.String(), "${{") {
// look up env var with that name. remove ${{}} and space
k := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(val, "${{"), "}}"))
v := LookUpEnv(k)
if v != "" {
val = v
}
}
properties[c.Name] = val
} }
return properties return properties
@ -172,6 +197,8 @@ func loadPubSub(tc TestComponent) pubsub.PubSub {
switch tc.Component { switch tc.Component {
case "redis": case "redis":
pubsub = p_redis.NewRedisStreams(testLogger) pubsub = p_redis.NewRedisStreams(testLogger)
case "azure-servicebus":
pubsub = p_servicebus.NewAzureServiceBus(testLogger)
default: default:
return nil return nil
} }
@ -198,6 +225,8 @@ func loadStateStore(tc TestComponent) state.Store {
switch tc.Component { switch tc.Component {
case "redis": case "redis":
store = s_redis.NewRedisStateStore(testLogger) store = s_redis.NewRedisStateStore(testLogger)
case "cosmosdb":
store = s_cosmosdb.NewCosmosDBStateStore(testLogger)
default: default:
return nil return nil
} }

View File

@ -1,9 +1,12 @@
package conformance package conformance
import ( import (
"os"
"testing" "testing"
"github.com/dapr/dapr/pkg/apis/components/v1alpha1"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
) )
func TestDecodeYaml(t *testing.T) { func TestDecodeYaml(t *testing.T) {
@ -40,3 +43,36 @@ func TestIsYaml(t *testing.T) {
resp = isYaml("test.exe") resp = isYaml("test.exe")
assert.False(t, resp) assert.False(t, resp)
} }
func TestLookUpEnv(t *testing.T) {
os.Setenv("CONF_TEST_KEY", "testval")
defer os.Unsetenv("CONF_TEST_KEY")
r := LookUpEnv("CONF_TEST_KEY")
assert.Equal(t, "testval", r)
r = LookUpEnv("CONF_TEST_NOT_THERE")
assert.Equal(t, "", r)
}
func TestConvertMetadataToProperties(t *testing.T) {
items := []v1alpha1.MetadataItem{
{
Name: "test_key",
Value: v1alpha1.DynamicValue{
JSON: v1.JSON{Raw: []byte("test")},
},
},
{
Name: "env_var_sub",
Value: v1alpha1.DynamicValue{
JSON: v1.JSON{Raw: []byte("${{CONF_TEST_KEY}}")},
},
},
}
os.Setenv("CONF_TEST_KEY", "testval")
defer os.Unsetenv("CONF_TEST_KEY")
resp := ConvertMetadataToProperties(items)
assert.NotNil(t, resp)
assert.Equal(t, 2, len(resp))
assert.Equal(t, "test", resp["test_key"])
assert.Equal(t, "testval", resp["env_var_sub"])
}