Skip to content

Commit

Permalink
Initial implementation of the GSI provider
Browse files Browse the repository at this point in the history
This provider allows Terraform AWS user to manage GSI on dynamo table as
a separate resource as a workaround for
hashicorp/terraform-provider-aws#671.

Using this new resource requires ignoring `global_secondary_index` and
`attribute` and the aws_dynamodb_table resource as described in the
example terraform file.

This provder also provides an `auto_import` mode which should make the
process of migration more seamless.
  • Loading branch information
kopatsy committed Mar 1, 2022
1 parent e6bd882 commit 7abb44d
Show file tree
Hide file tree
Showing 19 changed files with 824 additions and 114 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This GitHub action can publish assets for release when a tag is created.
# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
#
# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your
# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE`
# secret. If you would rather own your own GPG handling, please fork this action
# or use an alternative one for key handling.
#
# You will need to pass the `--batch` flag to `gpg` in your signing step
# in `goreleaser` to indicate this is being used in a non-interactive mode.
#
name: release
on:
push:
tags:
- 'v*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/[email protected]
-
name: Unshallow
run: git fetch --prune --unshallow
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
-
name: Import GPG key
id: import_gpg
uses: hashicorp/[email protected]
env:
# These secrets will need to be configured for the repository:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
PASSPHRASE: ${{ secrets.PASSPHRASE }}
-
name: Run GoReleaser
uses: goreleaser/[email protected]
with:
version: latest
args: release --rm-dist
env:
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
# GitHub sets this automatically
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ override.tf.json
.terraformrc
terraform.rc
terraform

terraform-provider-gsi
60 changes: 60 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Visit https://goreleaser.com for documentation on how to customize this
# behavior.
before:
hooks:
# this is just an example and not a requirement for provider building/publishing
- go mod tidy
builds:
- env:
# goreleaser does not work with CGO, it could also complicate
# usage by users in CI/CD systems like Terraform Cloud where
# they are unable to install libraries.
- CGO_ENABLED=0
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
ldflags:
- '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
goos:
- freebsd
- windows
- linux
- darwin
goarch:
- amd64
- '386'
- arm
- arm64
ignore:
- goos: darwin
goarch: '386'
binary: '{{ .ProjectName }}_v{{ .Version }}'
archives:
- format: zip
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
checksum:
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
algorithm: sha256
signs:
- artifacts: checksum
args:
# if you are using this in a GitHub action or some other automated pipeline, you
# need to pass the batch flag to indicate its not interactive.
- "--batch"
- "--local-user"
- "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
- "--output"
- "${signature}"
- "--detach-sign"
- "${artifact}"
release:
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
# If you want to manually examine the release before its live, uncomment this line:
# draft: true
changelog:
skip: true
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
TEST?=$$(go list ./... | grep -v 'vendor')
HOSTNAME=verkada.com
HOSTNAME=hashicorp.com
NAMESPACE=verkada
NAME=gsi
BINARY=terraform-provider-${NAME}
VERSION=0.1
OS_ARCH="$(go env GOHOSTOS)_$(go env GOHOSTARCH)
OS_ARCH=$(shell go env GOHOSTOS)_$(shell go env GOHOSTARCH)

default: install

Expand All @@ -26,12 +26,12 @@ release:
GOOS=windows GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_windows_amd64

install: build
mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/$(OS_ARCH)
mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/$(OS_ARCH)

test:
go test -i $(TEST) || exit 1
echo $(TEST) | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4

testacc:
TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m
TF_ACC=1 AWS_DYNAMODB_ENDPOINT=http://localhost:8000/ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar go test $(TEST) -v $(TESTARGS) -timeout 120m
60 changes: 52 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,65 @@
# Terraform Provider Hashicups
# Terraform Provider GSI

## Overview

This provider is a workaround to a limitation of the AWS provider (and TF language) which prevents the management of global secondary index associated with an autoscaler.

The approach is to decouple the table resource (still owned by the AWS provider) from the GSI resource which ignoring the GSIs on the table.

```terraform
provider "gsi" {}
resource "aws_dynamodb_table" "test_table" {
name = "test_table"
read_capacity = 5
write_capacity = 5
hash_key = "UserId"
attribute {
name = "UserId"
type = "S"
}
lifecycle {
ignore_changes = [global_secondary_index]
}
}
resource "gsi_global_secondary_index" "test_index" {
name = "test_index"
table_name = aws_dynamodb_table.test_table.name
hash_key = "UserId"
hash_key_type = "S"
range_key = "OrderId"
range_key_type = "S"
read_capacity = 5
write_capacity = 5
projection_type = "KEYS_ONLY"
}
```

Since you might have a lot of existing GSIs already, you can use `auto_import = true` in the provider configuration. When set, the first create will automatically import the GSI if one with the same name exists. Note that it will not attempt to correct drift so it might be a two step process to get to a clean plan.

## Build

Run the following command to build the provider

```shell
go build -o terraform-provider-hashicups
go build -o terraform-provider-gsi
```

## Test sample configuration

First, build and install the provider.
## Run acceptance tests

```shell
make install
(cd docker-compose; docker-compose up -d)
make testacc
```

Then, run the following command to initialize the workspace and apply the sample configuration.
## Run unit tests

```shell
terraform init && terraform apply
make test
```
6 changes: 6 additions & 0 deletions docker-compose/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM openjdk:7
RUN mkdir -p opt/dynamodb
WORKDIR /opt/dynamodb
RUN wget https://s3.eu-central-1.amazonaws.com/dynamodb-local-frankfurt/dynamodb_local_latest.tar.gz -q -O - | tar -xz
EXPOSE 8000
ENTRYPOINT ["java", "-jar", "DynamoDBLocal.jar"]
4 changes: 4 additions & 0 deletions docker-compose/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dynamo:
image: amazon/dynamodb-local
ports:
- 8000:8000
26 changes: 26 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "gsi Provider"
subcategory: ""
description: |-
---

# gsi Provider





<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- **access_key** (String) AWS access key ID
- **auto_import** (Boolean) Automatically import on create, not recommended unless transitioning away from GSI created with the AWS resource
- **dynamodb_endpoint** (String) AWS dynamodb endpoint
- **profile** (String) AWS profile
- **region** (String) AWS region
- **secret_key** (String) AWS secret key ID
- **token** (String) AWS session token
40 changes: 40 additions & 0 deletions docs/resources/global_secondary_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "gsi_global_secondary_index Resource - terraform-provider-gsi"
subcategory: ""
description: |-
---

# gsi_global_secondary_index (Resource)





<!-- schema generated by tfplugindocs -->
## Schema

### Required

- **hash_key** (String) Hash key of the index.
- **hash_key_type** (String) Type of the hash key.
- **name** (String) Name of the index.
- **projection_type** (String) Projection type.
- **read_capacity** (Number) Read capacity for the index, untracked after creation if autoscaling is enabled.
- **table_name** (String) Name of the DynamoDB table to which the GSI is associated..
- **write_capacity** (Number) Write capacity for the table, untracked after creation if autoscaling is enabled.

### Optional

- **autoscaling_enabled** (Boolean) Whether capacity is controlled by an autoscaler.
- **id** (String) The ID of this resource.
- **non_key_attributes** (Set of String) Additional attributes to include based in the projection.
- **range_key** (String) Range key of the index.
- **range_key_type** (String) Type of the range key.

### Read-Only

- **arn** (String) ARN of the Global Secondary Index.


57 changes: 37 additions & 20 deletions examples/main.tf
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
terraform {
required_providers {
vinfra = {
version = "0.1"
source = "verkada/vinfra"
required_providers {
gsi = {
source = "verkada/gsi"
}
}
}
}

provider "vinfra" {
region = "us-west-2"
profile = "hiring"
auto_import = true
provider "aws" {
}

resource "vinfra_gsi" "sample" {
table_name = "sample-table"
name = "effectiveEntityId-creation-index-2"
hash_key = "effectiveEntityId"
non_key_attributes = [
"tokenHash",
resource "aws_dynamodb_table" "test_table" {
name = "test_table"
read_capacity = 5
write_capacity = 5
hash_key = "UserId"

attribute {
name = "UserId"
type = "S"
}

lifecycle {
ignore_changes = [global_secondary_index, attribute]
}
}

resource "gsi_global_secondary_index" "test_index" {
name = "test_index"
table_name = aws_dynamodb_table.test_table.name

hash_key = "UserId"
hash_key_type = "S"
range_key = "OrderId"
range_key_type = "S"

read_capacity = 5
write_capacity = 5
projection_type = "KEYS_ONLY"

depends_on = [
aws_dynamodb_table.test_table
]
range_key = "creation"
projection_type = "INCLUDE"
initial_read_capacity = 5
initial_write_capacity = 5
}
}
Loading

0 comments on commit 7abb44d

Please sign in to comment.