Skip to content

Commit

Permalink
Simplify Localstack setup by utilizing S3 endpoint support (#713)
Browse files Browse the repository at this point in the history
  • Loading branch information
seshachalam-yv authored Nov 9, 2023
1 parent 227c876 commit ac3e156
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 47 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ deploy-via-kustomize: manifests $(KUSTOMIZE)
# Modify the Helm template located at charts/druid/templates if any changes are required
.PHONY: deploy
deploy: $(SKAFFOLD)
$(SKAFFOLD) run -m etcd-druid
$(SKAFFOLD) run -m etcd-druid --kubeconfig=$(KUBECONFIG_PATH)

# Generate manifests e.g. CRD, RBAC etc.
.PHONY: manifests
Expand Down Expand Up @@ -167,7 +167,7 @@ kind-down: $(KIND)

.PHONY: deploy-localstack
deploy-localstack: $(KUBECTL)
BUCKET_NAME=$(BUCKET_NAME) ./hack/deploy-localstack.sh
./hack/deploy-localstack.sh

.PHONY: ci-e2e-kind
ci-e2e-kind:
Expand Down
78 changes: 78 additions & 0 deletions config/samples/druid_v1alpha1_etcd_localstack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
apiVersion: druid.gardener.cloud/v1alpha1
kind: Etcd
metadata:
name: etcd-test
labels:
app: etcd-statefulset
gardener.cloud/role: controlplane
role: test
spec:
selector:
matchLabels:
app: etcd-statefulset
gardener.cloud/role: controlplane
role: test
annotations:
app: etcd-statefulset
gardener.cloud/role: controlplane
# networking.gardener.cloud/to-dns: allowed
# networking.gardener.cloud/to-private-networks: allowed
# networking.gardener.cloud/to-public-networks: allowed
role: test
labels:
app: etcd-statefulset
gardener.cloud/role: controlplane
# networking.gardener.cloud/to-dns: allowed
# networking.gardener.cloud/to-private-networks: allowed
# networking.gardener.cloud/to-public-networks: allowed
role: test
etcd:
metrics: basic
defragmentationSchedule: "0 */24 * * *"
resources:
limits: { cpu: 500m, memory: 1Gi }
requests: { cpu: 100m, memory: 200Mi }
clientPort: 2379
serverPort: 2380
quota: 8Gi
# heartbeatDuration: 10s
backup:
port: 8080
fullSnapshotSchedule: "0 */24 * * *"
resources:
limits: { cpu: 200m, memory: 1Gi }
requests: { cpu: 23m, memory: 128Mi }
garbageCollectionPolicy: Exponential
garbageCollectionPeriod: 43200s
deltaSnapshotPeriod: 300s
deltaSnapshotMemoryLimit: 1Gi
store:
container: etcd-bucket
prefix: etcd-test
provider: S3
secretRef:
name: etcd-backup-aws
compression:
enabled: false
policy: "gzip"
leaderElection:
reelectionPeriod: 5s
etcdConnectionTimeout: 5s

sharedConfig:
autoCompactionMode: periodic
autoCompactionRetention: "30m"
# schedulingConstraints:
# affinity: {}
# topologySpreadConstraints:
# - maxSkew: 1
# topologyKey: topology.kubernetes.io/zone
# whenUnsatisfiable: DoNotSchedule
# labelSelector:
# matchLabels:
# app: etcd-statefulset

replicas: 3
# priorityClassName: priority-class-name
# storageClass: default
# storageCapacity: 10Gi
15 changes: 15 additions & 0 deletions config/samples/etcd-secret-localstack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
data:
accessKeyID: QUNDRVNTS0VZQVdTVVNFUg==
bucketName: ZXRjZC1idWNrZXQK
endpoint: aHR0cDovL2xvY2Fsc3RhY2suZGVmYXVsdDo0NTY2
region: dXMtZWFzdC0y
s3ForcePathStyle: dHJ1ZQ==
secretAccessKey: c0VjcmVUS2V5
kind: Secret
metadata:
labels:
garden.sapcloud.io/role: controlplane
role: main
name: etcd-backup-aws
type: Opaque
86 changes: 86 additions & 0 deletions docs/development/getting-started-locally-localstack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Getting Started with etcd-druid, LocalStack, and Kind

This guide provides step-by-step instructions on how to set up etcd-druid with [LocalStack](https://localstack.cloud/) and Kind on your local machine. LocalStack emulates AWS services locally, which allows the etcd cluster to interact with AWS S3 without the need for an actual AWS connection. This setup is ideal for local development and testing.

## Prerequisites

- Docker (installed and running)
- AWS CLI (version `>=1.29.0` or `>=2.13.0`)

## Environment Setup

### Step 1: Provision the Kind Cluster

Execute the command below to provision a `kind` cluster. This command also forwards port `4566` from the [kind cluster](hack/e2e-test/infrastructure/kind/cluster.yaml) to your local machine, enabling LocalStack access:

```bash
make kind-up
```

### Step 2: Deploy LocalStack

Deploy LocalStack onto the Kubernetes cluster using the command below:

```bash
make deploy-localstack
```

### Step 3: Set up an S3 Bucket

1. Set up the AWS CLI to interact with LocalStack by setting the necessary environment variables. This configuration redirects S3 commands to the LocalStack endpoint and provides the required credentials for authentication:

```bash
export AWS_ENDPOINT_URL_S3="http://localhost:4566"
export AWS_ACCESS_KEY_ID=ACCESSKEYAWSUSER
export AWS_SECRET_ACCESS_KEY=sEcreTKey
export AWS_DEFAULT_REGION=us-east-2
```

2. Create an S3 bucket for etcd-druid backup purposes:

```bash
aws s3api create-bucket --bucket etcd-bucket --region us-east-2 --create-bucket-configuration LocationConstraint=us-east-2 --acl private
```

### Step 4: Deploy etcd-druid

Deploy etcd-druid onto the Kind cluster using the command below:

```bash
make deploy
```

### Step 5: Configure etcd with LocalStack Store

Apply the required Kubernetes manifests to create an etcd custom resource (CR) and a secret for AWS credentials, facilitating LocalStack access:

```bash
export KUBECONFIG=hack/e2e-test/infrastructure/kind/kubeconfig
kubectl apply -f config/samples/druid_v1alpha1_etcd_localstack.yaml -f config/samples/etcd-secret-localstack.yaml
```

### Step 6: Reconcile the etcd

Initiate etcd reconciliation by annotating the etcd resource with the `gardener.cloud/operation=reconcile` annotation:

```bash
kubectl annotate etcd etcd-test gardener.cloud/operation=reconcile
```

Congratulations! You have successfully configured `etcd-druid`, `LocalStack`, and `kind` on your local machine. Inspect the etcd-druid logs and LocalStack to ensure the setup operates as anticipated.

To validate the buckets, execute the following command:

```bash
aws s3 ls etcd-bucket/etcd-test/v2/
```

### Cleanup

To dismantle the setup, execute the following command:

```bash
make kind-down
unset AWS_ENDPOINT_URL_S3 AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DEFAULT_REGION KUBECONFIG

```
2 changes: 1 addition & 1 deletion hack/ci-e2e-kind.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ kubectl wait --for=condition=ready node --all
export AWS_APPLICATION_CREDENTIALS_JSON="/tmp/aws.json"
echo "{ \"accessKeyID\": \"ACCESSKEYAWSUSER\", \"secretAccessKey\": \"sEcreTKey\", \"region\": \"us-east-2\", \"endpoint\": \"http://127.0.0.1:4566\", \"s3ForcePathStyle\": true, \"bucketName\": \"${BUCKET_NAME}\" }" >/tmp/aws.json

make deploy-localstack BUCKET_NAME="$BUCKET_NAME"
make deploy-localstack
make LOCALSTACK_HOST="localstack.default:4566" \
AWS_ACCESS_KEY_ID="ACCESSKEYAWSUSER" \
AWS_SECRET_ACCESS_KEY="sEcreTKey" \
Expand Down
37 changes: 1 addition & 36 deletions hack/deploy-localstack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,5 @@ set -o nounset
set -o pipefail

kubectl apply -f ./hack/e2e-test/infrastructure/localstack/localstack.yaml

cat <<EOF | kubectl apply -f -
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
rewrite name $BUCKET_NAME.localstack.default localstack.default.svc.cluster.local
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
EOF

kubectl delete pods -n kube-system -l k8s-app=kube-dns
kubectl delete pods -n kube-system -l k8s-app=kube-dns
kubectl wait --for=condition=ready pod -n kube-system -l k8s-app=kube-dns --timeout=120s
kubectl rollout status deploy/localstack
kubectl wait --for=condition=ready pod -l app=localstack --timeout=240s
15 changes: 7 additions & 8 deletions hack/e2e-test/infrastructure/overlays/aws/common/files/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

ENDPOINT_URL=""
if [[ -n "${LOCALSTACK_HOST}" ]]; then
ENDPOINT_URL=" --endpoint-url=http://${LOCALSTACK_HOST}"
export AWS_ENDPOINT_URL_S3="http://${LOCALSTACK_HOST}"
fi

# More information at https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
Expand Down Expand Up @@ -46,15 +45,15 @@ region = ${AWS_REGION}" > ${HOME}/.aws/config
}

function create_s3_bucket() {
result=$(aws ${ENDPOINT_URL} s3api get-bucket-location --bucket ${TEST_ID} 2>&1 || true)
result=$(aws s3api get-bucket-location --bucket ${TEST_ID} 2>&1 || true)
if [[ $result == *NoSuchBucket* ]]; then
echo "Creating S3 bucket ${TEST_ID} in region ${AWS_REGION}"
aws ${ENDPOINT_URL} s3api create-bucket --bucket ${TEST_ID} --region ${AWS_REGION} --create-bucket-configuration LocationConstraint=${AWS_REGION} --acl private
aws s3api create-bucket --bucket ${TEST_ID} --region ${AWS_REGION} --create-bucket-configuration LocationConstraint=${AWS_REGION} --acl private
# Block public access to the S3 bucket
aws ${ENDPOINT_URL} s3api put-public-access-block --bucket ${TEST_ID} --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
aws s3api put-public-access-block --bucket ${TEST_ID} --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
# Deny non-HTTPS requests to the S3 bucket, except for localstack which is exposed on an HTTP endpoint
if [[ -z "${LOCALSTACK_HOST}" ]]; then
aws ${ENDPOINT_URL} s3api put-bucket-policy --bucket ${TEST_ID} --policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"s3:*\",\"Resource\":[\"arn:aws:s3:::${TEST_ID}\",\"arn:aws:s3:::${TEST_ID}/*\"],\"Condition\":{\"Bool\":{\"aws:SecureTransport\":\"false\"},\"NumericLessThan\":{\"s3:TlsVersion\":\"1.2\"}}}]}"
aws s3api put-bucket-policy --bucket ${TEST_ID} --policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"s3:*\",\"Resource\":[\"arn:aws:s3:::${TEST_ID}\",\"arn:aws:s3:::${TEST_ID}/*\"],\"Condition\":{\"Bool\":{\"aws:SecureTransport\":\"false\"},\"NumericLessThan\":{\"s3:TlsVersion\":\"1.2\"}}}]}"
fi
else
echo $result
Expand All @@ -66,10 +65,10 @@ function create_s3_bucket() {

function delete_s3_bucket() {
echo "About to delete S3 bucket ${TEST_ID}"
result=$(aws ${ENDPOINT_URL} s3api get-bucket-location --bucket ${TEST_ID} 2>&1 || true)
result=$(aws s3api get-bucket-location --bucket ${TEST_ID} 2>&1 || true)
if [[ $result == *NoSuchBucket* ]]; then
echo "Bucket is already gone."
return
fi
aws ${ENDPOINT_URL} s3 rb s3://${TEST_ID} --force
aws s3 rb s3://${TEST_ID} --force
}

0 comments on commit ac3e156

Please sign in to comment.