├── LICENSE ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── main.go ├── main.tf ├── scripts ├── changelog-links.sh ├── errcheck.sh ├── gofmtcheck.sh └── gogetcookie.sh └── snowflake ├── provider.go ├── provider_test.go ├── resource_database.go ├── resource_database_test.go ├── resource_grant.go ├── resource_grant_test.go ├── resource_user.go ├── resource_user_test.go ├── resource_warehouse.go ├── resource_warehouse_test.go └── utils.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, ShopRunner 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TEST?=$$(go list ./... |grep -v 'vendor') 2 | GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) 3 | 4 | default: build 5 | 6 | build: fmtcheck 7 | GO111MODULE=on go install 8 | 9 | test: fmtcheck 10 | go test -i $(TEST) || exit 1 11 | echo $(TEST) | \ 12 | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 13 | 14 | testacc: fmtcheck 15 | TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m 16 | 17 | vet: 18 | @echo "go vet ." 19 | @go vet $$(go list ./... | grep -v vendor/) ; if [ $$? -eq 1 ]; then \ 20 | echo ""; \ 21 | echo "Vet found suspicious constructs. Please check the reported constructs"; \ 22 | echo "and fix them if necessary before submitting the code for review."; \ 23 | exit 1; \ 24 | fi 25 | 26 | fmt: 27 | gofmt -w $(GOFMT_FILES) 28 | 29 | fmtcheck: 30 | @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" 31 | 32 | errcheck: 33 | @sh -c "'$(CURDIR)/scripts/errcheck.sh'" 34 | 35 | vendor-status: 36 | @govendor status 37 | 38 | test-compile: 39 | @if [ "$(TEST)" = "./..." ]; then \ 40 | echo "ERROR: Set TEST to a specific package. For example,"; \ 41 | echo " make test-compile TEST=./aws"; \ 42 | exit 1; \ 43 | fi 44 | go test -c $(TEST) $(TESTARGS) 45 | 46 | .PHONY: build test testacc vet fmt fmtcheck errcheck vendor-status test-compile -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Terraform Provider 2 | ================== 3 | 4 | - Website: https://www.terraform.io 5 | - [![Gitter chat](https://badges.gitter.im/hashicorp-terraform/Lobby.png)](https://gitter.im/hashicorp-terraform/Lobby) 6 | - Mailing list: [Google Groups](http://groups.google.com/group/terraform-tool) 7 | 8 | 9 | 10 | Requirements 11 | ------------ 12 | 13 | - [Terraform](https://www.terraform.io/downloads.html) 0.10.x 14 | - [Go](https://golang.org/doc/install) 1.8 (to build the provider plugin) 15 | 16 | Building The Provider 17 | --------------------- 18 | 19 | Clone repository to: `$GOPATH/src/github.com/terraform-providers/terraform-provider-$PROVIDER_NAME` 20 | 21 | ```sh 22 | $ mkdir -p $GOPATH/src/github.com/terraform-providers; cd $GOPATH/src/github.com/terraform-providers 23 | $ git clone git@github.com:terraform-providers/terraform-provider-$PROVIDER_NAME 24 | ``` 25 | 26 | Enter the provider directory and build the provider 27 | 28 | ```sh 29 | $ cd $GOPATH/src/github.com/terraform-providers/terraform-provider-$PROVIDER_NAME 30 | $ make build 31 | ``` 32 | 33 | Using the provider 34 | ---------------------- 35 | ## Fill in for each provider 36 | ``` 37 | go get github.com/snowflakedb/gosnowflake 38 | ``` 39 | 40 | Developing the Provider 41 | --------------------------- 42 | 43 | If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (version 1.8+ is *required*). You'll also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding `$GOPATH/bin` to your `$PATH`. 44 | 45 | To compile the provider, run `make build`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. 46 | 47 | ```sh 48 | $ make build 49 | ... 50 | $ $GOPATH/bin/terraform-provider-$PROVIDER_NAME 51 | ... 52 | ``` 53 | 54 | 55 | Provider Configuration 56 | -------------------------- 57 | 58 | Firstly, to use the provider you will need to create a user within Snowflake that can execute the resource requests made by Terraform 59 | 60 | Account type defaults to standard, but can be either standard or enterprise. 61 | 62 | ```sh 63 | $ export SF_USER 64 | $ export SF_PASSWORD 65 | $ export SF_REGION 66 | $ export SF_ACCOUNT 67 | $ export SF_ACCOUNT_TYPE 68 | ``` 69 | 70 | ### Snowflake Warehouse Management 71 | ``` 72 | resource "snowflake_warehouse" "warehouse_terraform" { 73 | name = "dev_wh" 74 | warehouse_size = "SMALL" 75 | auto_resume = false 76 | auto_suspend = 600 77 | comment = "terraform development warehouse" 78 | } 79 | ``` 80 | 81 | ##### Properties 82 | | Property | Description | Type | Required | 83 | | ------ | ------ | ------ | ------ | 84 | | `name` | Name of the Snowflake warehouse | String | TRUE | 85 | | `max_concurrency_level` | Max concurrent SQL statements that can run on warehouse | String | FALSE | 86 | | `statement_queued_timeout_in_seconds` | Time, in seconds, an SQL statement can be queued before being cancelled | String | FALSE | 87 | | `statement_timeout_in_seconds` | Time, in seconds, after which an SQL statement will be terminated | String | FALSE | 88 | | `warehouse_size` | Size of the warehouse | String | FALSE | 89 | | `max_cluster_count` | Max number of warehouses | String | FALSE | 90 | | `min_cluster_count` | Min number of warehouses | String | FALSE | 91 | | `auto_resume` | Should warehouse should auto resume | Boolean | FALSE | 92 | | `auto_suspend` | Should warehouse should auto suspend | Boolean | FALSE | 93 | | `initially_suspended` | Should warehouse start off suspended | Boolean | FALSE | 94 | | `resource_monitor` | Name of resource monitor assigned to warehouse | Boolean | FALSE | 95 | | `comment` | Additional comments | String | FALSE | 96 | 97 | ### Snowflake Database Management 98 | ``` 99 | resource "snowflake_database" "database_terraform" { 100 | name = "dev_db" 101 | comment = "terraform development database" 102 | } 103 | ``` 104 | 105 | ##### Properties 106 | | Property | Description | Type | Required | 107 | | ------ | ------ | ------ | ------ | 108 | | `name` | Name of the Snowflake database | String | TRUE | 109 | | `comment` | Additional comments | String | FALSE | 110 | 111 | ### Snowflake User Management 112 | ``` 113 | resource "snowflake_user" "tf_test_user" { 114 | user = "terraform.test" 115 | host = "mydomain.org" 116 | plaintext_password = "12345QWERTYqwerty" 117 | default_role = "READONLY" 118 | } 119 | ``` 120 | 121 | ##### Properties 122 | | Property | Description | Type | Required | 123 | | ------ | ------ | ------ | ------ | 124 | | `user` | The username of the user | String | TRUE | 125 | | `host` | Host/TLD associated with the user. The default for this is localhost. This has a direct effect on the Username | String | FALSE | 126 | | `plaintext_password` | Password of the user. Ensure that passwords conform to the complexity requirements by Snowflake | String | TRUE | 127 | | `default_role` | Default role the user assumes. Defaults to `null` | String | FALSE | 128 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ShopRunner/terraform-provider-snowflake 2 | 3 | require ( 4 | github.com/SermoDigital/jose v0.9.2-0.20180104203859-803625baeddc // indirect 5 | github.com/apparentlymart/go-cidr v1.0.0 // indirect 6 | github.com/blang/semver v3.5.1+incompatible // indirect 7 | github.com/google/uuid v1.1.0 // indirect 8 | github.com/hashicorp/errwrap v1.0.0 9 | github.com/hashicorp/go-getter v1.1.0 // indirect 10 | github.com/hashicorp/go-hclog v0.7.0 // indirect 11 | github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104 // indirect 12 | github.com/hashicorp/go-uuid v1.0.1 // indirect 13 | github.com/hashicorp/go-version v1.1.0 14 | github.com/hashicorp/hcl v1.0.0 // indirect 15 | github.com/hashicorp/hcl2 v0.0.0-20190214115825-fb2bc46cdbe3 // indirect 16 | github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93 // indirect 17 | github.com/hashicorp/logutils v1.0.0 // indirect 18 | github.com/hashicorp/terraform v0.11.11 19 | github.com/mitchellh/cli v1.0.0 // indirect 20 | github.com/mitchellh/copystructure v1.0.0 // indirect 21 | github.com/mitchellh/go-homedir v1.1.0 // indirect 22 | github.com/mitchellh/hashstructure v1.0.0 // indirect 23 | github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 // indirect 24 | github.com/snowflakedb/gosnowflake v1.1.16 25 | github.com/zclconf/go-cty v0.0.0-20190212192503-19dda139b164 // indirect 26 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2 // indirect 27 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd // indirect 28 | ) 29 | 30 | go 1.13 31 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/SermoDigital/jose v0.9.1 h1:atYaHPD3lPICcbK1owly3aPm0iaJGSGPi0WD4vLznv8= 2 | github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= 3 | github.com/SermoDigital/jose v0.9.2-0.20180104203859-803625baeddc h1:MhBvG7RLaLqlyjxMR6of35vt6MVQ+eXMcgn9X/sy0FE= 4 | github.com/SermoDigital/jose v0.9.2-0.20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= 5 | github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= 6 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 7 | github.com/apparentlymart/go-cidr v1.0.0 h1:lGDvXx8Lv9QHjrAVP7jyzleG4F9+FkRhJcEsDFxeb8w= 8 | github.com/apparentlymart/go-cidr v1.0.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= 9 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= 10 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= 11 | github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= 12 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= 13 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= 14 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 15 | github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY= 16 | github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= 17 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= 18 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= 19 | github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= 20 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 21 | github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= 22 | github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 23 | github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= 24 | github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= 25 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 27 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 28 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= 29 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 30 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 31 | github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= 32 | github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 33 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 34 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 35 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 36 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 37 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= 39 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 40 | github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= 41 | github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 42 | github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 43 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 44 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 45 | github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= 46 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 47 | github.com/hashicorp/go-getter v1.1.0 h1:iGVeg7L4V5FTFV3D6w+1NAyvth7BIWWSzD60pWloe2Q= 48 | github.com/hashicorp/go-getter v1.1.0/go.mod h1:q+PoBhh16brIKwJS9kt18jEtXHTg2EGkmrA9P7HVS+U= 49 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= 50 | github.com/hashicorp/go-hclog v0.7.0 h1:TwD6x3r9IdHnoVSBmfvEgKKLRu08augeYi8fwWcbmiE= 51 | github.com/hashicorp/go-hclog v0.7.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 52 | github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= 53 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 54 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 55 | github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104 h1:9iQ/zrTOJqzP+kH37s6xNb6T1RysiT7fnDD3DJbspVw= 56 | github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= 57 | github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= 58 | github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= 59 | github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= 60 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 61 | github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= 62 | github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 63 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 64 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 65 | github.com/hashicorp/hcl2 v0.0.0-20190214115825-fb2bc46cdbe3 h1:vBYzM7Cv4Z0yBsyWHHQjf2jG/avkJgumzryMbpO563E= 66 | github.com/hashicorp/hcl2 v0.0.0-20190214115825-fb2bc46cdbe3/go.mod h1:HtEzazM5AZ9fviNEof8QZB4T1Vz9UhHrGhnMPzl//Ek= 67 | github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93 h1:T1Q6ag9tCwun16AW+XK3tAql24P4uTGUMIn1/92WsQQ= 68 | github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE= 69 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= 70 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 71 | github.com/hashicorp/terraform v0.11.11 h1:5q1y/a0RB1QmKc1n6E9tnWQqPMb+nEb7Bfol74N2grw= 72 | github.com/hashicorp/terraform v0.11.11/go.mod h1:uN1KUiT7Wdg61fPwsGXQwK3c8PmpIVZrt5Vcb1VrSoM= 73 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= 74 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 75 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 76 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 77 | github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= 78 | github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 79 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 80 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 81 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 82 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 83 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 84 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= 85 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 86 | github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= 87 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 88 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 89 | github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= 90 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 91 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 92 | github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= 93 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 94 | github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= 95 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 96 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 97 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 98 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 99 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 100 | github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= 101 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 102 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= 103 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 104 | github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= 105 | github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= 106 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 107 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 108 | github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= 109 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 110 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 111 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 112 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 113 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 114 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 115 | github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= 116 | github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= 117 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 118 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 119 | github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= 120 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 121 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 122 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 123 | github.com/snowflakedb/gosnowflake v1.1.16 h1:8e9cPxEWnmc+RSMe14MpaV+QlvYEk9iptgk7kTuWggc= 124 | github.com/snowflakedb/gosnowflake v1.1.16/go.mod h1:NsRq2QeiMUuoNUJhp5Q6xGC4uBrsS9g6LwZVEkTWgsE= 125 | github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 126 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 127 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 128 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 129 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 130 | github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= 131 | github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= 132 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 133 | github.com/zclconf/go-cty v0.0.0-20190124225737-a385d646c1e9/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= 134 | github.com/zclconf/go-cty v0.0.0-20190212192503-19dda139b164 h1:H/K552vgSGWF1LOtDOsBa+i9ZttdABUfACdsQZ87/Ds= 135 | github.com/zclconf/go-cty v0.0.0-20190212192503-19dda139b164/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= 136 | golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 137 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2 h1:NwxKRvbkH5MsNkvOtPZi3/3kmI8CAzs3mtv+GLQMkNo= 138 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 139 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 140 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 141 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 142 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 143 | golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 144 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= 145 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 146 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 147 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 148 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= 149 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 150 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 151 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 152 | golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 153 | golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ= 154 | golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 155 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 156 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 157 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 158 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 159 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 160 | google.golang.org/grpc v1.14.0 h1:ArxJuB1NWfPY6r9Gp9gqwplT0Ge7nqv9msgu03lHLmo= 161 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 162 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 163 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 164 | gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 165 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 166 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 167 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 168 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 169 | howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= 170 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ShopRunner/terraform-provider-snowflake/snowflake" 5 | "github.com/hashicorp/terraform/plugin" 6 | ) 7 | 8 | func main() { 9 | plugin.Serve(&plugin.ServeOpts{ProviderFunc: snowflake.Provider}) 10 | } 11 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | resource "snowflake_warehouse" "warehouse_terraform" { 2 | name = "dev_wh" 3 | warehouse_size = "SMALL" 4 | auto_resume = false 5 | auto_suspend = 600 6 | comment = "terraform development warehouse" 7 | } 8 | 9 | resource "snowflake_database" "database_terraform" { 10 | name = "dev_db" 11 | comment = "terraform development database" 12 | } 13 | -------------------------------------------------------------------------------- /scripts/changelog-links.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script rewrites [GH-nnnn]-style references in the CHANGELOG.md file to 4 | # be Markdown links to the given github issues. 5 | # 6 | # This is run during releases so that the issue references in all of the 7 | # released items are presented as clickable links, but we can just use the 8 | # easy [GH-nnnn] shorthand for quickly adding items to the "Unrelease" section 9 | # while merging things between releases. 10 | 11 | set -e 12 | 13 | if [[ ! -f CHANGELOG.md ]]; then 14 | echo "ERROR: CHANGELOG.md not found in pwd." 15 | echo "Please run this from the root of the terraform provider repository" 16 | exit 1 17 | fi 18 | 19 | if [[ `uname` == "Darwin" ]]; then 20 | echo "Using BSD sed" 21 | SED="sed -i.bak -E -e" 22 | else 23 | echo "Using GNU sed" 24 | SED="sed -i.bak -r -e" 25 | fi 26 | 27 | PROVIDER_URL="https:\/\/github.com\/sadhasivam\/terraform-provider-snowflake\/issues" 28 | 29 | $SED "s/GH-([0-9]+)/\[#\1\]\($PROVIDER_URL\/\1\)/g" -e 's/\[\[#(.+)([0-9])\)]$/(\[#\1\2))/g' CHANGELOG.md 30 | 31 | rm CHANGELOG.md.bak 32 | -------------------------------------------------------------------------------- /scripts/errcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check gofmt 4 | echo "==> Checking for unchecked errors..." 5 | 6 | if ! which errcheck > /dev/null; then 7 | echo "==> Installing errcheck..." 8 | go get -u github.com/kisielk/errcheck 9 | fi 10 | 11 | err_files=$(errcheck -ignoretests \ 12 | -ignore 'github.com/hashicorp/terraform/helper/schema:Set' \ 13 | -ignore 'bytes:.*' \ 14 | -ignore 'io:Close|Write' \ 15 | $(go list ./...| grep -v /vendor/)) 16 | 17 | if [[ -n ${err_files} ]]; then 18 | echo 'Unchecked errors found in the following places:' 19 | echo "${err_files}" 20 | echo "Please handle returned errors. You can check directly with \`make errcheck\`" 21 | exit 1 22 | fi 23 | 24 | exit 0 25 | -------------------------------------------------------------------------------- /scripts/gofmtcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check gofmt 4 | echo "==> Checking that code complies with gofmt requirements..." 5 | gofmt_files=$(gofmt -l `find . -name '*.go' | grep -v vendor`) 6 | if [[ -n ${gofmt_files} ]]; then 7 | echo 'gofmt needs running on the following files:' 8 | echo "${gofmt_files}" 9 | echo "You can use the command: \`make fmt\` to reformat code." 10 | exit 1 11 | fi 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /scripts/gogetcookie.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | touch ~/.gitcookies 4 | chmod 0600 ~/.gitcookies 5 | 6 | git config --global http.cookiefile ~/.gitcookies 7 | 8 | tr , \\t <<\__END__ >>~/.gitcookies 9 | .googlesource.com,TRUE,/,TRUE,2147483647,o,git-paul.hashicorp.com=1/z7s05EYPudQ9qoe6dMVfmAVwgZopEkZBb1a2mA5QtHE 10 | __END__ 11 | -------------------------------------------------------------------------------- /snowflake/provider.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "strings" 7 | 8 | _ "github.com/snowflakedb/gosnowflake" 9 | 10 | "github.com/hashicorp/go-version" 11 | "github.com/hashicorp/terraform/helper/schema" 12 | "github.com/hashicorp/terraform/terraform" 13 | ) 14 | 15 | // DefaultSnowFlakeRegion mentions SnowFlake Account Region 16 | const DefaultSnowFlakeRegion = "us-west-2" 17 | 18 | // DefaultAccountType is the account type for the Snowflake Account ("standard" or "enterprise") 19 | const DefaultAccountType = "standard" 20 | 21 | type providerConfiguration struct { 22 | DB *sql.DB 23 | ServerVersion *version.Version 24 | AccountType string 25 | } 26 | 27 | // Provider blah foo bar 28 | func Provider() terraform.ResourceProvider { 29 | return &schema.Provider{ 30 | Schema: map[string]*schema.Schema{ 31 | "account": &schema.Schema{ 32 | Type: schema.TypeString, 33 | Required: true, 34 | DefaultFunc: schema.EnvDefaultFunc("SF_ACCOUNT", nil), 35 | Description: "Name of Snowflake Account string to connect to", 36 | ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 37 | value := v.(string) 38 | if value == "" { 39 | errors = append(errors, fmt.Errorf("Account must not be an empty string")) 40 | } 41 | return 42 | }, 43 | }, 44 | "username": &schema.Schema{ 45 | Type: schema.TypeString, 46 | Required: true, 47 | DefaultFunc: schema.EnvDefaultFunc("SF_USER", nil), 48 | Description: "Snowflake user name to connect as ", 49 | ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 50 | value := v.(string) 51 | if value == "" { 52 | errors = append(errors, fmt.Errorf("Username must not be an empty string")) 53 | } 54 | return 55 | }, 56 | }, 57 | "password": &schema.Schema{ 58 | Type: schema.TypeString, 59 | Required: true, 60 | Description: "Password to be used to connect to Snowflake", 61 | DefaultFunc: schema.EnvDefaultFunc("SF_PASSWORD", nil), 62 | }, 63 | "region": &schema.Schema{ 64 | Type: schema.TypeString, 65 | Optional: true, 66 | Description: "Snowflake region that is configured with account", 67 | DefaultFunc: schema.EnvDefaultFunc("SF_REGION", DefaultSnowFlakeRegion), 68 | }, 69 | "account_type": &schema.Schema{ 70 | Type: schema.TypeString, 71 | Optional: true, 72 | Description: "Snowflake account type (standard or enterprise)", 73 | DefaultFunc: schema.EnvDefaultFunc("SF_ACCOUNT_TYPE", DefaultAccountType), 74 | }, 75 | }, 76 | 77 | ResourcesMap: map[string]*schema.Resource{ 78 | "snowflake_warehouse": resourceWarehouse(), 79 | "snowflake_database": resourceDatabase(), 80 | "snowflake_user": resourceUser(), 81 | // "snowflake_grant": resourceGrant(), 82 | }, 83 | 84 | ConfigureFunc: providerConfigure, 85 | } 86 | } 87 | 88 | func providerConfigure(d *schema.ResourceData) (interface{}, error) { 89 | 90 | var username = d.Get("username").(string) 91 | var password = d.Get("password").(string) 92 | var account = d.Get("account").(string) 93 | var region = d.Get("region").(string) 94 | var accountType = d.Get("account_type").(string) 95 | 96 | // database/sql is the thread-safe by default, so we can 97 | // safely re-use the same handle between multiple parallel 98 | // operations. 99 | 100 | var dataSourceName string 101 | 102 | dataSourceName = fmt.Sprintf("%s:%s@%s.%s", username, password, account, region) 103 | 104 | db, err := sql.Open("snowflake", dataSourceName) 105 | 106 | ver, err := serverVersion(db) 107 | if err != nil { 108 | return nil, err 109 | } 110 | 111 | return &providerConfiguration{ 112 | DB: db, 113 | ServerVersion: ver, 114 | AccountType: accountType, 115 | }, nil 116 | } 117 | 118 | var identQuoteReplacer = strings.NewReplacer("`", "``") 119 | 120 | func quoteIdentifier(in string) string { 121 | return fmt.Sprintf("`%s`", identQuoteReplacer.Replace(in)) 122 | } 123 | 124 | func serverVersion(db *sql.DB) (*version.Version, error) { 125 | rows, err := db.Query("SELECT CURRENT_VERSION()") 126 | if err != nil { 127 | return nil, err 128 | } 129 | if !rows.Next() { 130 | return nil, fmt.Errorf("SELECT CURRENT_VERSION() returned an empty set") 131 | } 132 | 133 | var versionString string 134 | rows.Scan(&versionString) 135 | return version.NewVersion(versionString) 136 | } 137 | -------------------------------------------------------------------------------- /snowflake/provider_test.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "github.com/hashicorp/terraform/helper/schema" 5 | "github.com/hashicorp/terraform/terraform" 6 | "testing" 7 | ) 8 | 9 | var testSnowflakeProviders map[string]terraform.ResourceProvider 10 | var testSnowflakeProvider *schema.Provider 11 | 12 | func init() { 13 | testSnowflakeProvider = Provider().(*schema.Provider) 14 | testSnowflakeProviders = map[string]terraform.ResourceProvider{ 15 | "snowflakedb": testSnowflakeProvider, 16 | } 17 | } 18 | 19 | func TestProvider(t *testing.T) { 20 | if err := Provider().(*schema.Provider).InternalValidate(); err != nil { 21 | t.Fatalf("err: %s", err) 22 | } 23 | } 24 | 25 | func TestProvider_impl(t *testing.T) { 26 | var _ terraform.ResourceProvider = Provider() 27 | } 28 | -------------------------------------------------------------------------------- /snowflake/resource_database.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "bytes" 5 | "database/sql" 6 | "fmt" 7 | "log" 8 | 9 | "github.com/hashicorp/errwrap" 10 | "github.com/hashicorp/terraform/helper/schema" 11 | ) 12 | 13 | const unknownDatabaseErrCode = 1049 14 | const ( 15 | dbNameAttr = "name" 16 | dbCommentAttr = "comment" 17 | ) 18 | 19 | func resourceDatabase() *schema.Resource { 20 | return &schema.Resource{ 21 | Create: createDatabase, 22 | Update: updateDatabase, 23 | Read: readDatabase, 24 | Delete: deleteDatabase, 25 | 26 | Schema: map[string]*schema.Schema{ 27 | dbNameAttr: { 28 | Type: schema.TypeString, 29 | Required: true, 30 | ForceNew: false, 31 | Description: "Identifier for the Snowflake database", 32 | }, 33 | dbCommentAttr: { 34 | Type: schema.TypeString, 35 | Optional: true, 36 | Default: "", 37 | ForceNew: false, 38 | Description: "Specifies a comment for the database", 39 | }, 40 | }, 41 | } 42 | } 43 | 44 | func createDatabase(d *schema.ResourceData, meta interface{}) error { 45 | dbName := d.Get(dbNameAttr).(string) 46 | db := meta.(*providerConfiguration).DB 47 | b := bytes.NewBufferString("CREATE DATABASE IF NOT EXISTS ") 48 | fmt.Fprint(b, dbName) 49 | fmt.Fprintf(b, " ") 50 | // Wrap string values in quotes 51 | for _, attr := range []string{dbCommentAttr} { 52 | fmt.Fprintf(b, " %s='%v' ", attr, d.Get(attr)) 53 | } 54 | 55 | sql := b.String() 56 | if _, err := db.Exec(sql); err != nil { 57 | return errwrap.Wrapf(fmt.Sprintf("Error creating database sql(%s) \n %q: {{err}}", sql, dbName), err) 58 | } 59 | d.SetId(dbName) 60 | return readDatabase(d, meta) 61 | } 62 | 63 | func updateDatabase(d *schema.ResourceData, meta interface{}) error { 64 | dbName := d.Get(dbNameAttr).(string) 65 | db := meta.(*providerConfiguration).DB 66 | b := bytes.NewBufferString("ALTER DATABASE IF EXISTS ") 67 | fmt.Fprint(b, dbName) 68 | fmt.Fprintf(b, " SET ") 69 | // Wrap string values in quotes 70 | for _, attr := range []string{dbCommentAttr} { 71 | fmt.Fprintf(b, " %s='%v' ", attr, d.Get(attr)) 72 | } 73 | 74 | sql := b.String() 75 | if _, err := db.Exec(sql); err != nil { 76 | return errwrap.Wrapf(fmt.Sprintf("Error altering database %q: {{err}}", dbName), err) 77 | } 78 | d.SetId(dbName) 79 | return readDatabase(d, meta) 80 | } 81 | 82 | func readDatabase(d *schema.ResourceData, meta interface{}) error { 83 | db := meta.(*providerConfiguration).DB 84 | 85 | databaseName := d.Id() 86 | stmtSQL := fmt.Sprintf("SHOW DATABASES LIKE '%s'", databaseName) 87 | 88 | fmt.Printf(" Read Database Executing query: %s \n", stmtSQL) 89 | log.Println("Executing query:", stmtSQL) 90 | 91 | var createdOn, name, isDefault, isCurrent, origin, owner, comment, options, retentionTime sql.NullString 92 | 93 | err := db.QueryRow(stmtSQL).Scan( 94 | &createdOn, &name, &isDefault, &isCurrent, &origin, &owner, &comment, &options, &retentionTime, 95 | ) 96 | 97 | if err != nil { 98 | return fmt.Errorf("Error during show databases like: %s", err) 99 | } 100 | 101 | d.Set(dbNameAttr, name) 102 | d.Set(dbCommentAttr, comment) 103 | return nil 104 | } 105 | 106 | func deleteDatabase(d *schema.ResourceData, meta interface{}) error { 107 | db := meta.(*providerConfiguration).DB 108 | dbName := d.Get(dbNameAttr).(string) 109 | sql := fmt.Sprintf("DROP DATABASE %s ", dbName) 110 | if _, err := db.Exec(sql); err != nil { 111 | return errwrap.Wrapf(fmt.Sprintf("Error dropping database %q: {{err}}", dbName), err) 112 | } 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /snowflake/resource_database_test.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform/helper/resource" 7 | ) 8 | 9 | func TestDatabaseSnowflakeDatabase(t *testing.T) { 10 | resource.Test(t, resource.TestCase{ 11 | Providers: testSnowflakeProviders, 12 | Steps: []resource.TestStep{ 13 | { 14 | Config: testSnowflakeDatabaseConfig, 15 | Check: resource.ComposeTestCheckFunc( 16 | resource.TestCheckResourceAttr( 17 | "snowflake_database", "name", "shoprunner_terraform_db"), 18 | resource.TestCheckResourceAttr("snowflake_database", "comment", "A test comment"), 19 | ), 20 | }, 21 | }, 22 | }) 23 | } 24 | 25 | var testSnowflakeDatabaseConfig = ` 26 | resource "snowflake_database" "shoprunner_database_terraform" { 27 | name = "shoprunner_terraform_db" 28 | comment = "A test comment" 29 | } 30 | ` 31 | -------------------------------------------------------------------------------- /snowflake/resource_grant.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | "github.com/hashicorp/terraform/helper/schema" 9 | ) 10 | 11 | const nonexistingGrantErrCode = 1141 12 | 13 | func resourceGrant() *schema.Resource { 14 | return &schema.Resource{ 15 | Create: CreateGrant, 16 | Update: nil, 17 | Read: ReadGrant, 18 | Delete: DeleteGrant, 19 | 20 | Schema: map[string]*schema.Schema{ 21 | "user": &schema.Schema{ 22 | Type: schema.TypeString, 23 | Required: true, 24 | ForceNew: true, 25 | }, 26 | 27 | "host": &schema.Schema{ 28 | Type: schema.TypeString, 29 | Optional: true, 30 | ForceNew: true, 31 | Default: "localhost", 32 | }, 33 | 34 | "database": &schema.Schema{ 35 | Type: schema.TypeString, 36 | Required: true, 37 | ForceNew: true, 38 | }, 39 | 40 | "privileges": &schema.Schema{ 41 | Type: schema.TypeSet, 42 | Required: true, 43 | ForceNew: true, 44 | Elem: &schema.Schema{Type: schema.TypeString}, 45 | Set: schema.HashString, 46 | }, 47 | 48 | "grant": &schema.Schema{ 49 | Type: schema.TypeBool, 50 | Optional: true, 51 | ForceNew: true, 52 | Default: false, 53 | }, 54 | }, 55 | } 56 | } 57 | 58 | func CreateGrant(d *schema.ResourceData, meta interface{}) error { 59 | db := meta.(*providerConfiguration).DB 60 | 61 | // create a comma-delimited string of privileges 62 | var privileges string 63 | var privilegesList []string 64 | vL := d.Get("privileges").(*schema.Set).List() 65 | for _, v := range vL { 66 | privilegesList = append(privilegesList, v.(string)) 67 | } 68 | privileges = strings.Join(privilegesList, ",") 69 | 70 | stmtSQL := fmt.Sprintf("GRANT %s on %s.* TO '%s'@'%s'", 71 | privileges, 72 | d.Get("database").(string), 73 | d.Get("user").(string), 74 | d.Get("host").(string)) 75 | 76 | if d.Get("grant").(bool) { 77 | stmtSQL += " WITH GRANT OPTION" 78 | } 79 | 80 | log.Println("Executing statement:", stmtSQL) 81 | _, err := db.Exec(stmtSQL) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | user := fmt.Sprintf("%s@%s:%s", d.Get("user").(string), d.Get("host").(string), d.Get("database")) 87 | d.SetId(user) 88 | 89 | return ReadGrant(d, meta) 90 | } 91 | 92 | func ReadGrant(d *schema.ResourceData, meta interface{}) error { 93 | db := meta.(*providerConfiguration).DB 94 | 95 | stmtSQL := fmt.Sprintf("SHOW GRANTS FOR '%s'@'%s'", 96 | d.Get("user").(string), 97 | d.Get("host").(string)) 98 | 99 | log.Println("Executing statement:", stmtSQL) 100 | 101 | rows, err := db.Query(stmtSQL) 102 | if err != nil { 103 | d.SetId("") 104 | } else { 105 | rows.Close() 106 | } 107 | return nil 108 | } 109 | 110 | func DeleteGrant(d *schema.ResourceData, meta interface{}) error { 111 | db := meta.(*providerConfiguration).DB 112 | 113 | stmtSQL := fmt.Sprintf("REVOKE GRANT OPTION ON %s.* FROM '%s'@'%s'", 114 | d.Get("database").(string), 115 | d.Get("user").(string), 116 | d.Get("host").(string)) 117 | 118 | log.Println("Executing statement:", stmtSQL) 119 | _, err := db.Query(stmtSQL) 120 | if err != nil { 121 | return err 122 | } 123 | 124 | stmtSQL = fmt.Sprintf("REVOKE ALL ON %s.* FROM '%s'@'%s'", 125 | d.Get("database").(string), 126 | d.Get("user").(string), 127 | d.Get("host").(string)) 128 | 129 | log.Println("Executing statement:", stmtSQL) 130 | _, err = db.Exec(stmtSQL) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /snowflake/resource_grant_test.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform/helper/resource" 7 | ) 8 | 9 | func TestDatabaseSnowflakeGrant(t *testing.T) { 10 | resource.Test(t, resource.TestCase{ 11 | Providers: testSnowflakeProviders, 12 | Steps: []resource.TestStep{ 13 | { 14 | Config: testSnowflakeGrantConfig, 15 | Check: resource.ComposeTestCheckFunc( 16 | resource.TestCheckResourceAttr( 17 | "snowflake_grant", "user", "shoprunner_terraform_user"), 18 | resource.TestCheckResourceAttr( 19 | "snowflake_grant", "host", "192.168.0.1"), 20 | resource.TestCheckResourceAttr( 21 | "snowflake_grant", "database", "db"), 22 | resource.TestCheckResourceAttrSet( 23 | "snowflake_grant", "privileges"), 24 | resource.TestCheckResourceAttr( 25 | "snowflake_grant", "grant", "false"), 26 | ), 27 | }, 28 | }, 29 | }) 30 | } 31 | 32 | var testSnowflakeGrantConfig = ` 33 | resource "snowflake_grant" "shoprunner_grant_terraform" { 34 | user = "shoprunner_terraform_user" 35 | host = "192.168.0.1" 36 | database = "db" 37 | privileges = ["all"] 38 | } 39 | ` 40 | -------------------------------------------------------------------------------- /snowflake/resource_user.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hashicorp/terraform/helper/schema" 6 | "log" 7 | ) 8 | 9 | func resourceUser() *schema.Resource { 10 | return &schema.Resource{ 11 | Create: CreateUser, 12 | Update: UpdateUser, 13 | Read: ReadUser, 14 | Delete: DeleteUser, 15 | 16 | Schema: map[string]*schema.Schema{ 17 | "user": &schema.Schema{ 18 | Type: schema.TypeString, 19 | Required: true, 20 | ForceNew: true, 21 | }, 22 | 23 | "host": &schema.Schema{ 24 | Type: schema.TypeString, 25 | Optional: true, 26 | ForceNew: true, 27 | Default: "localhost", 28 | }, 29 | 30 | "plaintext_password": &schema.Schema{ 31 | Type: schema.TypeString, 32 | Optional: true, 33 | Sensitive: true, 34 | StateFunc: hashSum, 35 | }, 36 | "password": &schema.Schema{ 37 | Type: schema.TypeString, 38 | Optional: true, 39 | ConflictsWith: []string{"plaintext_password"}, 40 | Sensitive: true, 41 | Deprecated: "Please use plaintext_password instead", 42 | }, 43 | "default_role": &schema.Schema{ 44 | Type: schema.TypeString, 45 | Optional: true, 46 | Default: "NULL", 47 | }, 48 | }, 49 | } 50 | } 51 | 52 | func CreateUser(d *schema.ResourceData, meta interface{}) error { 53 | db := meta.(*providerConfiguration).DB 54 | 55 | stmtSQL := fmt.Sprintf("CREATE USER \"%s@%s\"", 56 | d.Get("user").(string), 57 | d.Get("host").(string)) 58 | 59 | var password string 60 | if v, ok := d.GetOk("plaintext_password"); ok { 61 | password = v.(string) 62 | } else { 63 | password = d.Get("password").(string) 64 | } 65 | 66 | if password != "" { 67 | stmtSQL = stmtSQL + fmt.Sprintf(" PASSWORD = \"%s\"", password) 68 | } 69 | 70 | stmtSQL = stmtSQL + fmt.Sprintf(" DEFAULT_ROLE = \"%s\"", d.Get("default_role").(string)) 71 | 72 | log.Println("Executing statement:", stmtSQL) 73 | _, err := db.Exec(stmtSQL) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | user := fmt.Sprintf("%s@%s", d.Get("user").(string), d.Get("host").(string)) 79 | d.SetId(user) 80 | 81 | return nil 82 | } 83 | 84 | func UpdateUser(d *schema.ResourceData, meta interface{}) error { 85 | conf := meta.(*providerConfiguration) 86 | 87 | var newpw interface{} 88 | if d.HasChange("plaintext_password") { 89 | _, newpw = d.GetChange("plaintext_password") 90 | } else if d.HasChange("password") { 91 | _, newpw = d.GetChange("password") 92 | } else { 93 | newpw = nil 94 | } 95 | 96 | var newdefrole interface{} 97 | if d.HasChange("default_role") { 98 | _, newdefrole = d.GetChange("default_role") 99 | } else { 100 | newdefrole = nil 101 | } 102 | 103 | if newpw != nil || newdefrole != nil { 104 | stmtSQL := fmt.Sprintf("ALTER USER \"%s@%s\" SET ", 105 | d.Get("user").(string), 106 | d.Get("host").(string)) 107 | 108 | if newpw != nil { 109 | stmtSQL = stmtSQL + fmt.Sprintf(" PASSWORD = \"%s\"", 110 | newpw.(string)) 111 | } 112 | 113 | if newdefrole != nil { 114 | stmtSQL = stmtSQL + fmt.Sprintf(" DEFAULT_ROLE = \"%s\"", 115 | newdefrole.(string)) 116 | } 117 | 118 | log.Println("Executing query:", stmtSQL) 119 | _, err := conf.DB.Exec(stmtSQL) 120 | if err != nil { 121 | return err 122 | } 123 | } 124 | 125 | return nil 126 | } 127 | 128 | func ReadUser(d *schema.ResourceData, meta interface{}) error { 129 | db := meta.(*providerConfiguration).DB 130 | 131 | stmtSQL := fmt.Sprintf("SHOW USERS LIKE '%s@%s'", 132 | d.Get("user").(string), 133 | d.Get("host").(string)) 134 | 135 | log.Println("Executing statement:", stmtSQL) 136 | 137 | rows, err := db.Query(stmtSQL) 138 | if err != nil { 139 | return err 140 | } 141 | defer rows.Close() 142 | 143 | if !rows.Next() && rows.Err() == nil { 144 | d.SetId("") 145 | } 146 | return rows.Err() 147 | } 148 | 149 | func DeleteUser(d *schema.ResourceData, meta interface{}) error { 150 | db := meta.(*providerConfiguration).DB 151 | 152 | stmtSQL := fmt.Sprintf("DROP USER \"%s@%s\"", 153 | d.Get("user").(string), 154 | d.Get("host").(string)) 155 | 156 | log.Println("Executing statement:", stmtSQL) 157 | 158 | _, err := db.Exec(stmtSQL) 159 | if err == nil { 160 | d.SetId("") 161 | } 162 | return err 163 | } 164 | -------------------------------------------------------------------------------- /snowflake/resource_user_test.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "github.com/hashicorp/terraform/helper/resource" 5 | "testing" 6 | ) 7 | 8 | func TestUserSnowflakeDatabase(t *testing.T) { 9 | resource.Test(t, resource.TestCase{ 10 | Providers: testSnowflakeProviders, 11 | Steps: []resource.TestStep{ 12 | { 13 | Config: testSnowflakeUserConfig, 14 | Check: resource.ComposeTestCheckFunc( 15 | resource.TestCheckResourceAttr( 16 | "snowflake_user", "name", "shoprunner_terraform"), 17 | resource.TestCheckResourceAttr("snowflake_user", "user", "tf-test"), 18 | ), 19 | }, 20 | }, 21 | }) 22 | } 23 | 24 | var testSnowflakeUserConfig = ` 25 | resource "snowflake_user" "shoprunner_terraform" { 26 | user = "tf-test" 27 | } 28 | ` 29 | -------------------------------------------------------------------------------- /snowflake/resource_warehouse.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | 8 | "bytes" 9 | 10 | "github.com/hashicorp/errwrap" 11 | "github.com/hashicorp/terraform/helper/schema" 12 | ) 13 | 14 | const ( 15 | whNameAttr = "name" 16 | whMaxConcurrencyLevelAttr = "max_concurrency_level" 17 | whStatementQueuedTimeOutInSecondsAttr = "statement_queued_timeout_in_seconds" 18 | whStatementTimeoutInSecondsAttr = "statement_timeout_in_seconds" 19 | whCommentAttr = "comment" 20 | 21 | //Properties 22 | whSizeAttr = "warehouse_size" 23 | whMulticlusterEnabled = "multicluster_enabled" 24 | whMaxClusterCount = "max_cluster_count" 25 | whMinClusterCount = "min_cluster_count" 26 | whAutoSuspend = "auto_suspend" 27 | whAutoResume = "auto_resume" 28 | whInitiallySuspended = "initially_suspended" 29 | whResourceMonitor = "resource_monitor" 30 | 31 | //whUUIDAttr = "UUID" 32 | ) 33 | 34 | func resourceWarehouse() *schema.Resource { 35 | return &schema.Resource{ 36 | Create: createWarehouse, 37 | Update: updateWarehouse, 38 | Read: readWarehouse, 39 | Delete: deleteWarehouse, 40 | 41 | Schema: map[string]*schema.Schema{ 42 | whNameAttr: { 43 | Type: schema.TypeString, 44 | Required: true, 45 | ForceNew: false, 46 | Description: "Identifier for the Snowflake warehouse; must be unique for your account", 47 | }, 48 | whMaxConcurrencyLevelAttr: { 49 | Type: schema.TypeInt, 50 | Optional: true, 51 | Default: 1, 52 | ForceNew: false, 53 | Description: "Specifies the maximum number of SQL statements (queries, DDL, DML, etc.) a warehouse cluster can execute concurrently", 54 | }, 55 | whStatementQueuedTimeOutInSecondsAttr: { 56 | Type: schema.TypeInt, 57 | Optional: true, 58 | Default: 120, 59 | ForceNew: false, 60 | Description: "Specifies the time, in seconds, a SQL statement (query, DDL, DML, etc.) can be queued on a warehouse before it is canceled by the system", 61 | }, 62 | whStatementTimeoutInSecondsAttr: { 63 | Type: schema.TypeInt, 64 | Optional: true, 65 | Default: 1000, 66 | ForceNew: false, 67 | Description: "Specifies the time, in seconds, after which a running SQL statement (query, DDL, DML, etc.) is canceled by the system", 68 | }, 69 | whCommentAttr: { 70 | Type: schema.TypeString, 71 | Optional: true, 72 | Default: "", 73 | ForceNew: false, 74 | Description: "Specifies a comment for the warehouse", 75 | }, 76 | whSizeAttr: { 77 | Type: schema.TypeString, 78 | Optional: true, 79 | Default: "XSMALL", 80 | ForceNew: false, 81 | Description: "Specifies the size of virtual warehouse to create", 82 | }, 83 | whMulticlusterEnabled: { 84 | Type: schema.TypeBool, 85 | Optional: true, 86 | Default: false, 87 | ForceNew: false, 88 | Description: "Specifies whether multicluster is enabled", 89 | }, 90 | whMaxClusterCount: { 91 | Type: schema.TypeInt, 92 | Optional: true, 93 | Default: 1, 94 | ForceNew: false, 95 | Description: "Specifies the maximum number of server clusters for a multi-cluster warehouse", 96 | }, 97 | whMinClusterCount: { 98 | Type: schema.TypeInt, 99 | Optional: true, 100 | Default: 1, 101 | ForceNew: false, 102 | Description: "Specifies the minimum number of server clusters for a multi-cluster warehouse", 103 | }, 104 | whAutoSuspend: { 105 | Type: schema.TypeInt, 106 | Optional: true, 107 | Default: 60, 108 | ForceNew: false, 109 | Description: "Specifies the number of seconds of inactivity after which a warehouse is automatically suspended", 110 | }, 111 | whAutoResume: { 112 | Type: schema.TypeBool, 113 | Optional: true, 114 | Default: true, 115 | ForceNew: false, 116 | Description: "Specifies whether to automatically resume a warehouse when it is accessed by a SQL statement", 117 | }, 118 | whInitiallySuspended: { 119 | Type: schema.TypeBool, 120 | Optional: true, 121 | Default: true, 122 | ForceNew: false, 123 | Description: "Specifies whether the warehouse is created initially in suspended state", 124 | }, 125 | whResourceMonitor: { 126 | Type: schema.TypeBool, 127 | Optional: true, 128 | Default: false, 129 | ForceNew: false, 130 | Description: "Specifies the name of a resource monitor that is explicitly assigned to the warehouse", 131 | }, 132 | }, 133 | } 134 | } 135 | 136 | func createWarehouse(d *schema.ResourceData, meta interface{}) error { 137 | whName := d.Get(whNameAttr).(string) 138 | db := meta.(*providerConfiguration).DB 139 | b := bytes.NewBufferString("CREATE WAREHOUSE IF NOT EXISTS ") 140 | fmt.Fprint(b, whName) 141 | fmt.Fprintf(b, " WITH ") 142 | // only add these parameters if multicluster is enabled 143 | if d.Get(whMulticlusterEnabled) == true { 144 | for _, attr := range []string{whMaxClusterCount, whMinClusterCount} { 145 | fmt.Fprintf(b, " %s=%v ", attr, d.Get(attr)) 146 | } 147 | } 148 | for _, attr := range []string{whAutoSuspend, whAutoResume, whInitiallySuspended} { 149 | fmt.Fprintf(b, " %s=%v ", attr, d.Get(attr)) 150 | } 151 | // Wrap string values in quotes 152 | for _, attr := range []string{whSizeAttr, whCommentAttr} { 153 | fmt.Fprintf(b, " %s='%v' ", attr, d.Get(attr)) 154 | } 155 | 156 | sql := b.String() 157 | if _, err := db.Exec(sql); err != nil { 158 | return errwrap.Wrapf(fmt.Sprintf("Error creating warehouse sql(%s) \n %q: {{err}}", sql, whName), err) 159 | } 160 | d.SetId(whName) 161 | return readWarehouse(d, meta) 162 | } 163 | 164 | func updateWarehouse(d *schema.ResourceData, meta interface{}) error { 165 | whName := d.Get(whNameAttr).(string) 166 | db := meta.(*providerConfiguration).DB 167 | b := bytes.NewBufferString("ALTER WAREHOUSE IF EXISTS ") 168 | fmt.Fprint(b, whName) 169 | fmt.Fprintf(b, " SET ") 170 | if d.Get(whMulticlusterEnabled) == true { 171 | for _, attr := range []string{whMaxClusterCount, whMinClusterCount} { 172 | fmt.Fprintf(b, " %s=%v ", attr, d.Get(attr)) 173 | } 174 | } 175 | for _, attr := range []string{whAutoSuspend, whAutoResume} { 176 | fmt.Fprintf(b, " %s=%v ", attr, d.Get(attr)) 177 | } 178 | // Wrap string values in quotes 179 | for _, attr := range []string{whSizeAttr, whCommentAttr} { 180 | fmt.Fprintf(b, " %s='%v' ", attr, d.Get(attr)) 181 | } 182 | 183 | sql := b.String() 184 | if _, err := db.Exec(sql); err != nil { 185 | return errwrap.Wrapf(fmt.Sprintf("Error altering warehouse %q: {{err}}", whName), err) 186 | } 187 | d.SetId(whName) 188 | return readWarehouse(d, meta) 189 | } 190 | 191 | func readWarehouse(d *schema.ResourceData, meta interface{}) error { 192 | db := meta.(*providerConfiguration).DB 193 | 194 | warehouseName := d.Id() 195 | stmtSQL := fmt.Sprintf("SHOW WAREHOUSES LIKE '%s'", warehouseName) 196 | 197 | fmt.Printf(" Read Warehouse Executing query: %s \n", stmtSQL) 198 | log.Println("Executing query:", stmtSQL) 199 | var name, state, instanceType, size, minClusterCount, maxClusterCount, startedClusters, running, queued sql.NullString 200 | var isDefault, isCurrent, autoSuspend, autoResume, available, provisioning, quiescing, other sql.NullString 201 | var createdOn, resumedOn, updatedOn, owner, comment, resourceMonitor sql.NullString 202 | var actives, pendings, failed, suspended, uuid, scalingPolicy sql.NullString 203 | 204 | var err error 205 | 206 | if meta.(*providerConfiguration).AccountType == "enterprise" { 207 | err = db.QueryRow(stmtSQL).Scan( 208 | &name, &state, &instanceType, &size, &minClusterCount, &maxClusterCount, &startedClusters, &scalingPolicy, &running, &queued, 209 | &isDefault, &isCurrent, &autoSuspend, &autoResume, &available, &provisioning, &quiescing, &other, 210 | &createdOn, &resumedOn, &updatedOn, &owner, &comment, &resourceMonitor, 211 | &actives, &pendings, &failed, &suspended, &uuid, 212 | ) 213 | } else { 214 | err = db.QueryRow(stmtSQL).Scan( 215 | &name, &state, &instanceType, &size, &running, &queued, 216 | &isDefault, &isCurrent, &autoSuspend, &autoResume, &available, &provisioning, &quiescing, &other, 217 | &createdOn, &resumedOn, &updatedOn, &owner, &comment, &resourceMonitor, 218 | &actives, &pendings, &failed, &suspended, &uuid, 219 | ) 220 | } 221 | 222 | if err != nil { 223 | return fmt.Errorf("Error during show create warehouse: %s", err) 224 | } 225 | 226 | d.Set(whNameAttr, name) 227 | d.Set("state", state) 228 | d.Set("instance", instanceType) 229 | d.Set(whSizeAttr, size) 230 | d.Set("running", running) 231 | d.Set("queued", queued) 232 | d.Set("is_default", isDefault) 233 | d.Set("is_current", isCurrent) 234 | d.Set("auto_suspend", autoSuspend) 235 | d.Set("auto_resume", autoResume) 236 | d.Set("available", available) 237 | d.Set("provisioning", provisioning) 238 | d.Set("quiescing", quiescing) 239 | d.Set("other", other) 240 | d.Set("create_on", createdOn) 241 | d.Set("resumed_on", resumedOn) 242 | d.Set("updated_on", updatedOn) 243 | d.Set("owner", owner) 244 | d.Set("comment", comment) 245 | d.Set("resource_monitor", resourceMonitor) 246 | d.Set("actives", actives) 247 | d.Set("pendings", pendings) 248 | d.Set("failed", failed) 249 | d.Set("suspended", suspended) 250 | d.Set("id", uuid) 251 | if meta.(*providerConfiguration).AccountType == "enterprise" { 252 | d.Set("min_cluster_count", minClusterCount) 253 | d.Set("max_cluster_count", maxClusterCount) 254 | d.Set("started_clusters", startedClusters) 255 | d.Set("scaling_policy", scalingPolicy) 256 | } 257 | return nil 258 | } 259 | 260 | func deleteWarehouse(d *schema.ResourceData, meta interface{}) error { 261 | db := meta.(*providerConfiguration).DB 262 | whName := d.Get(whNameAttr).(string) 263 | sql := fmt.Sprintf("DROP WAREHOUSE %s ", whName) 264 | if _, err := db.Exec(sql); err != nil { 265 | return errwrap.Wrapf(fmt.Sprintf("Error dropping warehouse %q: {{err}}", whName), err) 266 | } 267 | return nil 268 | } 269 | -------------------------------------------------------------------------------- /snowflake/resource_warehouse_test.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "github.com/hashicorp/terraform/helper/resource" 5 | "testing" 6 | ) 7 | 8 | func TestWarehouseSnowflakeDatabase(t *testing.T) { 9 | resource.Test(t, resource.TestCase{ 10 | Providers: testSnowflakeProviders, 11 | Steps: []resource.TestStep{ 12 | { 13 | Config: testSnowflakeWarehouseConfig, 14 | Check: resource.ComposeTestCheckFunc( 15 | resource.TestCheckResourceAttr( 16 | "snowflake_warehouse", "name", "shoprunner_terraform"), 17 | resource.TestCheckResourceAttr("snowflake_warehouse", "warehouse_size", "SMALL"), 18 | ), 19 | }, 20 | }, 21 | }) 22 | } 23 | 24 | var testSnowflakeWarehouseConfig = ` 25 | resource "snowflake_warehouse" "shoprunner_warehouse_terraform" { 26 | name = "shoprunner_terraform" 27 | warehouse_size = "SMALL" 28 | } 29 | ` 30 | -------------------------------------------------------------------------------- /snowflake/utils.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | ) 7 | 8 | func hashSum(contents interface{}) string { 9 | return fmt.Sprintf("%x", sha256.Sum256([]byte(contents.(string)))) 10 | } 11 | --------------------------------------------------------------------------------