RSQL is a command-line client for Redshift. Unlike the psql command-line, RSQL has control flow commands (IF, ELSE, GOTO, etc.) that are useful for ETL jobs. I want to run RSQL in a Fargate container so I call it from Step Functions ETL workflow. Overall this was fairly straight forward, but I’ll document it anyway.
Setup
In my use case, I am converting hundreds of Teradata BTEQ scripts to RSQL using the Schema Conversion Tool (SCT). These scripts have interdependencies and must be in the same the folder. Therefore, I copied them all to an EFS volume. If the scripts were stand alone, I would prefer to put them on S3.
First, I mounted the EFS volume on my development machine. I’m testing this on a Cloud9 instance.
Then, I created /mnt/efs/odbc.ini file with a single DSN that uses IAM permissions. I’m going to use a tak role in ECS and I have instance profile associated with development machine. Note that the RSQL documentation is a bit confusing here. The Instanceprofile=1 option will only work on an EC2 instance. However, if you omit it, RSQL (well, technically the ODBC driver) will use the IAM role on both EC2 and ECS. Honestly, I’m not really sure when you would use Instanceprofile=1.
For demo purposes, I also created /mnt/efs/test.rsql with the following that queries a table in the sample database.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT*FROMpublic.category;\if:ACTIVITYCOUNT=0\remark'****No data found****'\gotoLETSQUIT\else\remark'****Data found****'\gotoLETSDOSOMETHING\endif\labelLETSQUIT\remark'****We are quitting****'\exit0\labelLETSDOSOMETHING\remark'****We are doing it****'\exit0
Build the Container
Next, I create a Dockerfile that installs the RSQL command line. Note that the versions are hard coded here and should be updated to the latest versions. Also, note that the environment variables reference paths within the container. I’ll override them later to refer to files stored on the EFS volume. I didn;t want to couple the container image to my architecture.
Then, I tested it locally to make sure it works as expected. In the example below, -v /mnt/efs:/efs mounts the EFS volume in the container; -e ODBCINI=/efs/odbc.ini overrides the default environment variable to refer to my INI file stored on EFS; -D redshift-cluster-1 tells rsql which DSN to use; and -f /efs/test.rsql tells rsql which file to run.
In addition, I need a task definition. Note that the command, environment, and mountPoints correspond to the options we pass to docker run in the test above. Also note that I have created a ecs-admin role. You, of course, should never run as admin and should create a role with least privilege.
{"Comment":"Simple state machine that runs RSQL as an ECS Task.","StartAt":"RSQL","States":{"RSQL":{"Type":"Task","Resource":"arn:aws:states:::ecs:runTask","Parameters":{"LaunchType":"FARGATE","Cluster":"arn:aws:ecs:us-east-1:123456789012:cluster/rsql","TaskDefinition":"arn:aws:ecs:us-east-1:123456789012:task-definition/rsql","NetworkConfiguration":{"AwsvpcConfiguration":{"Subnets":["subnet-11111111","subnet-22222222"],"securityGroups":["sg-11111111"]"AssignPublicIp":"ENABLED"}},"Overrides":{"ContainerOverrides":[{"Name":"rsql","Command.$":"$.commands"}]}},"End":true}}}
Finally, notice the $.commands variable in the container override. This allows me to specify the RSQL command line options as an input the Step Functions state. For example, the following is identical the prior examples I have been using.