Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions apis/v1alpha1/ack-generate-metadata.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
ack_generate_info:
build_date: "2021-07-01T00:05:05Z"
build_date: "2021-07-01T01:00:26Z"
build_hash: 9fec5f08628d5dcc3af38d517ea014930b1ae39d
go_version: go1.15.2 darwin/amd64
version: v0.3.1
api_directory_checksum: 9b3b2aba266495c0066bcda32c685f5392eaa4c3
api_directory_checksum: a0e397840365dfef9f86900d211b96e5040cf6a9
api_version: v1alpha1
aws_sdk_go_version: ""
generator_config_info:
file_checksum: 4c9de6e616b479328ab112b111a77382551cee21
file_checksum: fa5597fbe4affa148d62ff2079b8d12ac1e07098
original_file_name: generator.yaml
last_modification:
reason: API generation
timestamp: 2021-07-01 00:06:30.140979 +0000 UTC
timestamp: 2021-07-01 01:00:35.012299 +0000 UTC
5 changes: 4 additions & 1 deletion apis/v1alpha1/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ resources:
from:
operation: CreateUser
path: AccessString
Passwords:
is_secret: true
compare:
is_ignored: true
hooks:
sdk_read_many_post_set_output:
code: "rm.setSyncedCondition(resp.Users[0].Status, &resource{ko})"
Expand Down Expand Up @@ -193,7 +197,6 @@ ignore:
- DescribeSnapshotsInput.ReplicationGroupId
- DescribeSnapshotsInput.SnapshotSource
- DescribeUsersInput.Engine
- CreateUserInput.Passwords #TODO: remove this once we have support for k8s secrets within slices
- ModifyUserInput.AccessString
- ModifyUserInput.NoPasswordRequired
- ModifyUserInput.Passwords
Expand Down
3 changes: 3 additions & 0 deletions apis/v1alpha1/user.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions config/crd/bases/elasticache.services.k8s.aws_users.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,28 @@ spec:
noPasswordRequired:
description: Indicates a password is not required for this user.
type: boolean
passwords:
description: Passwords used for this user. You can create up to two
passwords for each user.
items:
description: SecretKeyReference combines a k8s corev1.SecretReference
with a specific key within the referred-to Secret
properties:
key:
description: Key is the key within the secret
type: string
name:
description: Name is unique within a namespace to reference
a secret resource.
type: string
namespace:
description: Namespace defines the space within which the secret
name must be unique.
type: string
required:
- key
type: object
type: array
userID:
description: The ID of the user.
type: string
Expand Down
5 changes: 4 additions & 1 deletion generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ resources:
from:
operation: CreateUser
path: AccessString
Passwords:
is_secret: true
compare:
is_ignored: true
hooks:
sdk_read_many_post_set_output:
code: "rm.setSyncedCondition(resp.Users[0].Status, &resource{ko})"
Expand Down Expand Up @@ -200,7 +204,6 @@ ignore:
- DescribeSnapshotsInput.ReplicationGroupId
- DescribeSnapshotsInput.SnapshotSource
- DescribeUsersInput.Engine
- CreateUserInput.Passwords #TODO: remove this once we have support for k8s secrets within slices
- ModifyUserInput.AccessString
- ModifyUserInput.NoPasswordRequired
- ModifyUserInput.Passwords
Expand Down
2 changes: 1 addition & 1 deletion pkg/resource/user/post_build_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ func (rm *resourceManager) populateUpdatePayload(
input.NoPasswordRequired = r.ko.Spec.NoPasswordRequired
}

//TODO: add the passwords field here once we have secrets support for it
//TODO: add update for passwords field once we have framework-level support

}
17 changes: 17 additions & 0 deletions pkg/resource/user/sdk.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
16 changes: 16 additions & 0 deletions test/e2e/resources/user_password.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: elasticache.services.k8s.aws/v1alpha1
kind: User
metadata:
name: $USER_ID
spec:
accessString: $ACCESS_STRING
engine: redis
passwords:
- namespace: default
name: $NAME1
key: $KEY1
- namespace: default
name: $NAME2
key: $KEY2
userID: $USER_ID
userName: $USER_ID
49 changes: 21 additions & 28 deletions test/e2e/tests/test_replicationgroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,33 +87,35 @@ def rg_input_coverage(bootstrap_resources, make_rg_name, make_replication_group,
sleep(DEFAULT_WAIT_SECS)
rg_deletion_waiter.wait(ReplicationGroupId=input_dict["RG_ID"]) #throws exception if wait fails


@pytest.fixture(scope="module")
def first_secret():
k8s.create_opaque_secret("default", "first", "secret1", "securetoken123456")
yield
k8s.delete_secret("default", "first")

def secrets():
secrets = {
"NAME1": random_suffix_name("first", 32),
"NAME2": random_suffix_name("second", 32),
"KEY1": "secret1",
"KEY2": "secret2"
}
k8s.create_opaque_secret("default", secrets['NAME1'], secrets['KEY1'], random_suffix_name("token", 32))
k8s.create_opaque_secret("default", secrets['NAME2'], secrets['KEY2'], random_suffix_name("token", 32))
yield secrets

@pytest.fixture(scope="module")
def second_secret():
k8s.create_opaque_secret("default", "second", "secret2", "newsecuretoken123456")
yield
k8s.delete_secret("default", "second")
# teardown
k8s.delete_secret("default", secrets['NAME1'])
k8s.delete_secret("default", secrets['NAME2'])


@pytest.fixture(scope="module")
def rg_auth_token(make_rg_name, make_replication_group, rg_deletion_waiter, first_secret, second_secret):
def rg_auth_token(make_rg_name, make_replication_group, rg_deletion_waiter, secrets):
input_dict = {
"RG_ID": make_rg_name("rg-auth-token"),
"NAME": "first",
"KEY": "secret1"
"NAME": secrets['NAME1'],
"KEY": secrets['KEY1']
}
(reference, resource) = make_replication_group("replicationgroup_authtoken", input_dict, input_dict["RG_ID"])
yield (reference, resource)
k8s.delete_custom_resource(reference)
sleep(DEFAULT_WAIT_SECS)
rg_deletion_waiter.wait(ReplicationGroupId=input_dict["RG_ID"]) #throws exception if wait fails
rg_deletion_waiter.wait(ReplicationGroupId=input_dict["RG_ID"]) # throws exception if wait fails


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -230,22 +232,13 @@ def test_rg_cmd_update(self, rg_cmd_update_input, rg_cmd_update):
assert cc is not None
assert cc['EngineVersion'] == desired_engine_version

# TODO: remove annotation once https://github.com/aws-controllers-k8s/community/issues/745 is resolved
@pytest.mark.blocked
def test_rg_auth_token(self, rg_auth_token):
def test_rg_auth_token(self, rg_auth_token, secrets):
(reference, _) = rg_auth_token
assert k8s.wait_on_condition(reference, "ACK.ResourceSynced", "True", wait_periods=30)

update_dict = {
"RG_ID": reference.name,
"NAME": "second",
"KEY": "secret2"
}

updated_spec = load_elasticache_resource(
"replicationgroup_authtoken", additional_replacements=update_dict)

k8s.patch_custom_resource(reference, updated_spec)
patch = {"spec": {"authToken": {"name": secrets['NAME2'], "key": secrets['KEY2']}}}
k8s.patch_custom_resource(reference, patch)
sleep(DEFAULT_WAIT_SECS)
assert k8s.wait_on_condition(reference, "ACK.ResourceSynced", "True", wait_periods=30)

def test_rg_deletion(self, rg_deletion_input, rg_deletion, rg_deletion_waiter):
Expand Down
82 changes: 68 additions & 14 deletions test/e2e/tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,20 @@ def elasticache_client():

# set up input parameters for User
@pytest.fixture(scope="module")
def input_dict():
resource_name = random_suffix_name("test-user", 32)
input_dict = {
"USER_ID": resource_name,
def user_nopass_input():
return {
"USER_ID": random_suffix_name("user-nopass", 32),
"ACCESS_STRING": "on ~app::* -@all +@read"
}
return input_dict


@pytest.fixture(scope="module")
def user(input_dict, elasticache_client):
def user_nopass(user_nopass_input, elasticache_client):

# inject parameters into yaml; create User in cluster
user = load_elasticache_resource("user", additional_replacements=input_dict)
user = load_elasticache_resource("user_nopass", additional_replacements=user_nopass_input)
reference = k8s.CustomResourceReference(
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, input_dict["USER_ID"], namespace="default")
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, user_nopass_input["USER_ID"], namespace="default")
_ = k8s.create_custom_resource(reference, user)
resource = k8s.wait_resource_consumed_by_controller(reference)
assert resource is not None
Expand All @@ -60,22 +58,67 @@ def user(input_dict, elasticache_client):
k8s.delete_custom_resource(reference)
sleep(DEFAULT_WAIT_SECS)
with pytest.raises(botocore.exceptions.ClientError, match="UserNotFound"):
_ = elasticache_client.describe_users(UserId=input_dict["USER_ID"])
_ = elasticache_client.describe_users(UserId=user_nopass_input["USER_ID"])


# create secrets for below user password test
@pytest.fixture(scope="module")
def secrets():
secrets = {
"NAME1": random_suffix_name("first", 32),
"NAME2": random_suffix_name("second", 32),
"KEY1": "secret1",
"KEY2": "secret2"
}
k8s.create_opaque_secret("default", secrets['NAME1'], secrets['KEY1'], random_suffix_name("password", 32))
k8s.create_opaque_secret("default", secrets['NAME2'], secrets['KEY2'], random_suffix_name("password", 32))
yield secrets

# teardown
k8s.delete_secret("default", secrets['NAME1'])
k8s.delete_secret("default", secrets['NAME2'])


# input for test case with Passwords field
@pytest.fixture(scope="module")
def user_password_input(secrets):
inputs = {
"USER_ID": random_suffix_name("user-password", 32),
"ACCESS_STRING": "on ~app::* -@all +@read",
}
return {**secrets, **inputs}


@pytest.fixture(scope="module")
def user_password(user_password_input, elasticache_client):

# inject parameters into yaml; create User in cluster
user = load_elasticache_resource("user_password", additional_replacements=user_password_input)
reference = k8s.CustomResourceReference(
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, user_password_input["USER_ID"], namespace="default")
_ = k8s.create_custom_resource(reference, user)
resource = k8s.wait_resource_consumed_by_controller(reference)
assert resource is not None
yield (reference, resource)

# teardown: delete in k8s, assert user does not exist in AWS
k8s.delete_custom_resource(reference)
sleep(DEFAULT_WAIT_SECS)
with pytest.raises(botocore.exceptions.ClientError, match="UserNotFound"):
_ = elasticache_client.describe_users(UserId=user_password_input["USER_ID"])


@service_marker
class TestUser:

# TODO: add more scenarios once the passwords field is enabled

# CRUD test for User; "create" and "delete" operations implicit in "user" fixture
def test_CRUD(self, user, input_dict):
(reference, resource) = user
def test_user_nopass(self, user_nopass, user_nopass_input):
(reference, resource) = user_nopass
assert k8s.get_resource_exists(reference)

assert k8s.wait_on_condition(reference, "ACK.ResourceSynced", "True", wait_periods=5)
resource = k8s.get_resource(reference)
assert resource["status"]["lastRequestedAccessString"] == input_dict["ACCESS_STRING"]
assert resource["status"]["lastRequestedAccessString"] == user_nopass_input["ACCESS_STRING"]

new_access_string = "on ~app::* -@all +@read +@write"
user_patch = {"spec": {"accessString": new_access_string}}
Expand All @@ -87,3 +130,14 @@ def test_CRUD(self, user, input_dict):
assert resource["status"]["lastRequestedAccessString"] == new_access_string

#TODO: add terminal condition checks

# test creation with Passwords specified (as k8s secrets)
def test_user_password(self, user_password, user_password_input):
(reference, resource) = user_password
assert k8s.get_resource_exists(reference)

assert k8s.wait_on_condition(reference, "ACK.ResourceSynced", "True", wait_periods=5)
resource = k8s.get_resource(reference)
assert resource["status"]["authentication"] is not None
assert resource["status"]["authentication"]["type_"] == "password"
assert resource["status"]["authentication"]["passwordCount"] == 2