Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Backup resource custom hooks, terminalCodes and e2e tests #5

Merged
merged 1 commit into from
Jun 17, 2021
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
5 changes: 5 additions & 0 deletions generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ operations:
UpdateGlobalTable:
operation_type: Delete
resource_name: GlobalTable
DescribeBackup:
output_wrapper_field_path: BackupDescription.BackupDetails
resources:
Table:
exceptions:
Expand Down Expand Up @@ -32,3 +34,6 @@ resources:
errors:
404:
code: BackupNotFoundException
hooks:
sdk_read_one_post_set_output:
template_path: hooks/backup/sdk_read_one_post_set_output.go.tpl
60 changes: 60 additions & 0 deletions pkg/resource/backup/hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package backup

import (
"errors"

ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue"

"github.com/aws-controllers-k8s/dynamodb-controller/apis/v1alpha1"
)

var (
// TerminalStatuses are the status strings that are terminal states for a
// backup.
TerminalStatuses = []v1alpha1.BackupStatus_SDK{}
a-hilaly marked this conversation as resolved.
Show resolved Hide resolved
)

var (
requeueWaitWhileCreating = ackrequeue.NeededAfter(
errors.New("Backup in 'CREATING' state, cannot be modified or deleted."),
ackrequeue.DefaultRequeueAfterDuration,
)
)

// backupHasTerminalStatus returns whether the supplied backup is in a
// terminal state
func backupHasTerminalStatus(r *resource) bool {
if r.ko.Status.BackupStatus == nil {
return false
}
ts := *r.ko.Status.BackupStatus
for _, s := range TerminalStatuses {
if ts == string(s) {
return true
}
}
return false
}

// isBackupCreating returns true if the supplied Dynamodb backup is in the process
// of being created
func isBackupCreating(r *resource) bool {
if r.ko.Status.BackupStatus == nil {
return false
}
dbis := *r.ko.Status.BackupStatus
return dbis == string(v1alpha1.BackupStatus_SDK_CREATING)
}
43 changes: 42 additions & 1 deletion pkg/resource/backup/sdk.go

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

3 changes: 3 additions & 0 deletions templates/hooks/backup/sdk_read_one_post_set_output.go.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if isBackupCreating(&resource{ko}) {
return &resource{ko}, requeueWaitWhileCreating
}
7 changes: 7 additions & 0 deletions test/e2e/resources/backup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: dynamodb.services.k8s.aws/v1alpha1
kind: Backup
metadata:
name: $BACKUP_NAME
spec:
backupName: $BACKUP_NAME
tableName: $TABLE_NAME
143 changes: 143 additions & 0 deletions test/e2e/tests/test_backup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may
# not use this file except in compliance with the License. A copy of the
# License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.

import boto3
import pytest
import time
import logging
from typing import Dict, Tuple

from acktest.resources import random_suffix_name
from acktest.k8s import resource as k8s
from acktest.aws.identity import get_region
from e2e import (
service_marker,
CRD_GROUP,
CRD_VERSION,
load_dynamodb_resource,
wait_for_cr_status,
)
from e2e.replacement_values import REPLACEMENT_VALUES

RESOURCE_PLURAL = "backups"

DELETE_WAIT_AFTER_SECONDS = 10

@pytest.fixture(scope="module")
def dynamodb_client():
return boto3.client("dynamodb")

@pytest.fixture(scope="module")
def dynamodb_table():
resource_name = random_suffix_name("table", 32)

replacements = REPLACEMENT_VALUES.copy()
replacements["TABLE_NAME"] = resource_name

# load resource
resource_data = load_dynamodb_resource(
"table_forums",
additional_replacements=replacements,
)

table_reference = k8s.CustomResourceReference(
CRD_GROUP, CRD_VERSION, "tables",
resource_name, namespace="default",
)

# Create table
k8s.create_custom_resource(table_reference, resource_data)
table_resource = k8s.wait_resource_consumed_by_controller(table_reference)

assert table_resource is not None
assert k8s.get_resource_exists(table_reference)

wait_for_cr_status(
table_reference,
"tableStatus",
"ACTIVE",
10,
30,
)

yield (table_reference, table_resource)

_, deleted = k8s.delete_custom_resource(table_reference)
assert deleted

@service_marker
@pytest.mark.canary
class TestBackup:
def get_backup(self, dynamodb_client, backup_arn: str) -> dict:
try:
resp = dynamodb_client.describe_backup(
BackupArn=backup_arn,
)
return resp["BackupDescription"]

except Exception as e:
logging.debug(e)
return None

def backup_exists(self, dynamodb_client, backup_arn: str) -> bool:
return self.get_backup(dynamodb_client, backup_arn) is not None

def test_smoke(self, dynamodb_client, dynamodb_table):
(_, table_resource) = dynamodb_table
resource_name = random_suffix_name("backup", 32)
table_name = table_resource["spec"]["tableName"]

replacements = REPLACEMENT_VALUES.copy()
replacements["TABLE_NAME"] = table_name
replacements["BACKUP_NAME"] = resource_name

# Load Backup CR
resource_data = load_dynamodb_resource(
"backup",
additional_replacements=replacements,
)
logging.debug(resource_data)

# Create k8s resource
ref = k8s.CustomResourceReference(
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL,
resource_name, namespace="default",
)
k8s.create_custom_resource(ref, resource_data)
cr = k8s.wait_resource_consumed_by_controller(ref)

assert cr is not None
assert k8s.get_resource_exists(ref)

wait_for_cr_status(
ref,
"backupStatus",
"AVAILABLE",
10,
5,
)

backupArn = k8s.get_resource_arn(cr)
# Check DynamoDB Backup exists
exists = self.backup_exists(dynamodb_client, backupArn)
assert exists

# Delete k8s resource
_, deleted = k8s.delete_custom_resource(ref)
assert deleted is True

time.sleep(DELETE_WAIT_AFTER_SECONDS)

# Check DynamoDB Backup doesn't exists
exists = self.backup_exists(dynamodb_client, backupArn)
assert not exists