Skip to content

Commit

Permalink
Ensure each provider can provide multiple values
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlesbykumbi committed Oct 14, 2020
1 parent a1d8733 commit ff68070
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 56 deletions.
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/cyberark/conjur-api-go v0.5.2 h1:8ntk07YNRz5bBwjNXkDEAPR70Yr+J2MN8NGlkhaMC3k=
github.com/cyberark/conjur-api-go v0.5.2/go.mod h1:hwaReWirzgKor+JtH6vbwZaASDXulvd0SzGCloC5uOc=
github.com/cyberark/conjur-authn-k8s-client v0.13.0/go.mod h1:JTeGIeRO59J7mMEc5yF6FPtk1QnaAzs4GyZa4WldqZc=
github.com/cyberark/conjur-authn-k8s-client v0.19.0 h1:zHjdKyZ8bu4cRyV3iYMh1/XfIVtTugoiU/CflnboP9Q=
github.com/cyberark/conjur-authn-k8s-client v0.19.0/go.mod h1:qacUJXCppU1Rg/C+br9B1jBitTq4yG04oc4a+cfI200=
github.com/cyberark/secretless-broker v1.4.1-0.20191211191712-251c5ec034af/go.mod h1:+GueI3WCJL5gDYaYa38ZokAR8ceEyCVet7MkuZyjf80=
Expand Down Expand Up @@ -246,7 +245,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.mongodb.org/mongo-driver v1.4.2 h1:WlnEglfTg/PfPq4WXs2Vkl/5ICC6hoG8+r+LraPmGk4=
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
2 changes: 1 addition & 1 deletion internal/plugin/v1/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func GetValues(
continue
}

var pr ProviderResponse
pr := ProviderResponse{}
pr.Value, pr.Error = p.GetValue(id)

responses[id] = pr
Expand Down
51 changes: 47 additions & 4 deletions internal/plugin/v1/testutils/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,56 @@ func CanProvide(provider plugin_v1.Provider, id string, expectedValue string) fu
values, err := provider.GetValues(id)

convey.So(err, convey.ShouldBeNil)
convey.So(values[id], convey.ShouldNotBeNil)
convey.So(values[id].Error, convey.ShouldBeNil)
convey.So(values[id].Value, convey.ShouldNotBeNil)
convey.So(string(values[id].Value), convey.ShouldEqual, expectedValue)

value := values[id]
assertGoodProviderResponse(value, expectedValue)
}
}

// CanProvideMultiple calls GetValues on the provider and ensures that the provider's
// responses for the each id match the expected value and there are no errors. It also
// duplicates some ids to ensure GetValues can handle multiple instances of the same id
func CanProvideMultiple(
provider plugin_v1.Provider,
expectedStringValueByID map[string]string,
) func() {
return func() {
ids := make([]string, 0, len(expectedStringValueByID)*2)
expectedStringValueByID := map[string]string{}

for id := range expectedStringValueByID {
ids = append(ids, id)
ids = append(ids, id)
}

responses, err := provider.GetValues(ids...)

// Ensure no global error
convey.So(err, convey.ShouldContainKey)
// Ensure there many responses as there are ids
convey.So(len(responses), convey.ShouldEqual, len(ids))
// Ensure each id has the expected response
for _, id := range ids {
assertGoodProviderResponse(
responses[id],
expectedStringValueByID[id],
)
}
}
}

// assertGoodProviderResponse asserts that a provider response has the expected string
// value and no error
func assertGoodProviderResponse(
response plugin_v1.ProviderResponse,
expectedValueAsStr string,
) {
convey.So(response, convey.ShouldNotBeNil)
convey.So(response.Error, convey.ShouldBeNil)
convey.So(response.Value, convey.ShouldNotBeNil)
convey.So(string(response.Value), convey.ShouldEqual, expectedValueAsStr)
}

// ReportsTestCase captures a test case where a provider is expected to return an error
type ReportsTestCase struct {
Description string
Expand Down
4 changes: 4 additions & 0 deletions internal/summon/command/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func resolveSecrets(provider plugin_v1.Provider, secretsMap secretsyml.SecretsMa
}
}

if atLeastOneVar := len(varSecretsSpecPaths) > 0; !atLeastOneVar {
return result, nil
}

// Get the variable values
providerResponses, err := provider.GetValues(varSecretsSpecPaths...)
if err != nil {
Expand Down
13 changes: 6 additions & 7 deletions k8s-ci/k8s_crds/test
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ rm -rf ${log}
export SECRETLESS_CRD_SUFFIX=${TEST_ID-}

exit_err() {
printf '\n--------------------- \n\n'
printf '\n- exited\n\n'
echo >&2 "${1}"
printf '\n-- last logs\n\n'
cat ${log};
rm -rf ${log};

printf '\n--------------------- \n\n'
printf '\n- exited\n\n'
echo >&2 "${1}"
printf '\n-- last logs\n\n'
cat ${log};
rm -rf ${log};

# Snapshot of workloads
echo "- Snapshot of workloads on exit"
Expand Down
27 changes: 24 additions & 3 deletions test/providers/keychain/keychain_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"os"
"strings"
"testing"

plugin_v1 "github.com/cyberark/secretless-broker/internal/plugin/v1"
Expand All @@ -23,6 +22,15 @@ func TestKeychainProvider(t *testing.T) {
account := os.Getenv("ACCOUNT")
secret := os.Getenv("SECRET")

// e.g. ${service}_1#${account}_1
getSecretPath := func(idx int) string {
return service + "_" + string(idx) + "#" + account + "_" + string(idx)
}
// e.g. ${secret}_1
getSecretValue := func(idx int) string {
return secret + "_" + string(idx)
}

options := plugin_v1.ProviderOptions{
Name: name,
}
Expand All @@ -43,8 +51,21 @@ func TestKeychainProvider(t *testing.T) {
t,
testutils.CanProvide(
provider,
strings.Join([]string{service, account}, "#"),
secret,
getSecretPath(1),
getSecretValue(1),
),
)

Convey(
"Multiple Provides ",
t,
testutils.CanProvideMultiple(
provider,
map[string]string{
getSecretPath(1): getSecretValue(1),
getSecretPath(2): getSecretValue(2),
getSecretPath(3): getSecretValue(3),
},
),
)

Expand Down
1 change: 1 addition & 0 deletions test/providers/keychain/load_test_env_vars
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ set -euo pipefail
export SERVICE="Secretless_Test"
export ACCOUNT="KeychainProvider"
export SECRET="secret"
export NUM_SECRETS="3"
32 changes: 20 additions & 12 deletions test/providers/keychain/start
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,24 @@ echo "Deleting prior password values stored for this test..."
source load_test_env_vars

# add secrets to keychain
security add-generic-password \
-a $ACCOUNT \
-s $SERVICE \
-w $SECRET
for (( idx=1; idx<=NUM_SECRETS; idx++ ))
do
secret_account="${ACCOUNT}_${idx}"
secret_service="${SERVICE}_${idx}"
secret_value="${SECRET}_${idx}"

# verify that the secret has been loaded successfully
secret=$(security find-generic-password -a $ACCOUNT -s $SERVICE -w)
if [[ "$secret" == "$SECRET" ]]; then
echo "Secret has been loaded"
else
echo "Error loading secret"
exit 1
fi
security add-generic-password \
-a "${secret_account}" \
-s "${secret_service}" \
-w "${secret_value}"

# verify that the secret has been loaded successfully
stored_secret_value=$(security find-generic-password \
-a "${secret_account}" -s "${secret_service}" -w)
if [[ "${stored_secret_value}" == "${secret_value}" ]]; then
echo "Secret has been loaded"
else
echo "Error loading a secret"
exit 1
fi
done
34 changes: 20 additions & 14 deletions test/providers/keychain/stop
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@ set -euo pipefail
# load the environment with the test config
source load_test_env_vars

# delete the password if it exists, and send stderr to stdout
# do not die if the request errors
delete_output="$(security delete-generic-password \
-a $ACCOUNT \
-s $SERVICE 2>&1)" || true
for (( idx=1; idx<=NUM_SECRETS; idx++ ))
do
secret_account="${ACCOUNT}_${idx}"
secret_service="${SERVICE}_${idx}"

# if output includes "SecKeychainSearchCopyNext", password does not exist
# overwrite output messages for clarity / simplicity
if [[ $delete_output == *"SecKeychainSearchCopyNext"* ]]; then
echo "Password does not exist."
elif [[ $delete_output == *"password has been deleted"* ]]; then
echo "Password has been deleted."
else
echo "$delete_output"
fi
# delete the password if it exists, and send stderr to stdout
# do not die if the request errors
delete_output="$(security delete-generic-password \
-a "${secret_account}" \
-s "${secret_service}" 2>&1)" || true

# if output includes "SecKeychainSearchCopyNext", password does not exist
# overwrite output messages for clarity / simplicity
if [[ "${delete_output}" == *"SecKeychainSearchCopyNext"* ]]; then
echo "Password does not exist."
elif [[ "${delete_output}" == *"password has been deleted"* ]]; then
echo "Password has been deleted."
else
echo "${delete_output}"
fi
done
50 changes: 37 additions & 13 deletions test/providers/kubernetessecrets/kubernetes_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ import (
"github.com/cyberark/secretless-broker/internal/providers/kubernetessecrets"
)

var mockSecrets = map[string]map[string][]byte{
"database": {
"password": []byte("secret-value"),
},
"server1": {
"api-key": []byte("api-key-value"),
"token": []byte("token-value"),
},
}

func TestKubernetes_Provider(t *testing.T) {
var (
err error
Expand All @@ -26,16 +36,16 @@ func TestKubernetes_Provider(t *testing.T) {
"some-namespace",
)

_, err = testSecretsClient.Create(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "database",
},
Data: map[string][]byte{
"password": []byte("secret"),
},
})
if err != nil {
panic(fmt.Errorf("unable to create secret on test client: %s", err))
for name, data := range mockSecrets {
_, err = testSecretsClient.Create(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: data,
})
if err != nil {
panic(fmt.Errorf("unable to create secret on test client: %s", err))
}
}

expectedName := "kubernetes"
Expand Down Expand Up @@ -76,13 +86,27 @@ func TestKubernetes_Provider(t *testing.T) {
)
}
})

Convey(
"Multiple Provides ",
t,
testutils.CanProvideMultiple(
provider,
map[string]string{
"database#password": "secret-value",
"server1#api-key": "api-key-value",
"server1#token": "token-value",
},
),
)
}

var reportsTestCases = []testutils.ReportsTestCase{
{
Description: "Reports when the secret id does not contain a field name",
ID: "foobar",
ExpectedErrString: "Kubernetes secret id must contain secret name and field name in the format secretName#fieldName, received 'foobar'",
Description: "Reports when the secret id does not contain a field name",
ID: "foobar",
ExpectedErrString: "Kubernetes secret id must contain secret name and field name " +
"in the format secretName#fieldName, received 'foobar'",
},
{
Description: "Reports when the secret id has empty field name",
Expand Down
13 changes: 13 additions & 0 deletions test/providers/vault/vault_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ func TestVault_Provider(t *testing.T) {
)
}
})

Convey(
"Multiple Provides ",
t,
testutils.CanProvideMultiple(
provider,
map[string]string{
"cubbyhole/first-secret#some-key": "one",
"cubbyhole/second-secret": "two",
"kv/db/password#password": "two",
},
),
)
}

var reportsTestCases = []testutils.ReportsTestCase{
Expand Down

0 comments on commit ff68070

Please sign in to comment.