Update to latest 0.12 sdk. (#3656)
This commit is contained in:
parent
c7aa148d14
commit
ba0e969f36
2
go.mod
2
go.mod
|
@ -12,7 +12,7 @@ require (
|
|||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/hashicorp/go-version v1.1.0
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform v0.12.0-alpha4.0.20190417210818-177a7afb781f
|
||||
github.com/hashicorp/terraform v0.12.0-rc1.0.20190517222244-f19903073985
|
||||
github.com/hashicorp/vault v1.0.1 // indirect
|
||||
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a // indirect
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
|
|
46
go.sum
46
go.sum
|
@ -43,6 +43,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
|
|||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/aws/aws-sdk-go v1.16.36 h1:POeH34ZME++pr7GBGh+ZO6Y5kOwSMQpqp5BGUgooJ6k=
|
||||
github.com/aws/aws-sdk-go v1.16.36/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.19.18 h1:Hb3+b9HCqrOrbAtFstUWg7H5TQ+/EcklJtE8VShVs8o=
|
||||
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
|
@ -115,6 +117,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
|
@ -145,18 +148,18 @@ github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3m
|
|||
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-getter v1.1.0 h1:iGVeg7L4V5FTFV3D6w+1NAyvth7BIWWSzD60pWloe2Q=
|
||||
github.com/hashicorp/go-getter v1.1.0/go.mod h1:q+PoBhh16brIKwJS9kt18jEtXHTg2EGkmrA9P7HVS+U=
|
||||
github.com/hashicorp/go-getter v1.3.0 h1:pFMSFlI9l5NaeuzkpE3L7BYk9qQ9juTAgXW/H0cqxcU=
|
||||
github.com/hashicorp/go-getter v1.3.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw=
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20190322172744-52e1c4730856 h1:FHiCaU46W1WoqApsaGGIKbNkhQ6v71hJrOf2INQMLUo=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20190322172744-52e1c4730856/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-plugin v1.0.1-0.20190430211030-5692942914bb h1:Zg2pmmk0lrLFL85lQGt08bOUBpIBaVs6/psiAyx0c4w=
|
||||
github.com/hashicorp/go-plugin v1.0.1-0.20190430211030-5692942914bb/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||
|
@ -166,8 +169,7 @@ github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoD
|
|||
github.com/hashicorp/go-slug v0.3.0 h1:L0c+AvH/J64iMNF4VqRaRku2DMTEuHioPVS7kMjWIU8=
|
||||
github.com/hashicorp/go-slug v0.3.0/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
|
||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-tfe v0.3.14 h1:1eWmq4RAICGufydNUWu7ahb0gtq24pN9jatD2FkdxdE=
|
||||
github.com/hashicorp/go-tfe v0.3.14/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM=
|
||||
github.com/hashicorp/go-tfe v0.3.16/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
|
@ -179,8 +181,8 @@ github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh
|
|||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20181208003705-670926858200/go.mod h1:ShfpTh661oAaxo7VcNxg0zcZW6jvMa7Moy2oFx7e5dE=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20190416162332-2c5a4b7d729a h1:doKt9ZBCYgYQrGK6CqJsEB+8xqm3WoFyKu4TPZlyymg=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20190416162332-2c5a4b7d729a/go.mod h1:HtEzazM5AZ9fviNEof8QZB4T1Vz9UhHrGhnMPzl//Ek=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20190515223218-4b22149b7cef h1:xZRvbcwHY8zhaxDwgkmpAp2emwZkVn7p3gat0zhq2X0=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20190515223218-4b22149b7cef/go.mod h1:4oI94iqF3GB10QScn46WqbG0kgTUpha97SAzzg2+2ec=
|
||||
github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590 h1:2yzhWGdgQUWZUCNK+AoO35V+HTsgEmcM4J9IkArh7PI=
|
||||
github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
|
@ -188,8 +190,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
|||
github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
|
||||
github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb h1:ZbgmOQt8DOg796figP87/EFCVx2v2h9yRvwHF/zceX4=
|
||||
github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
|
||||
github.com/hashicorp/terraform v0.12.0-alpha4.0.20190417210818-177a7afb781f h1:Gl7Ig3E2XpntOCaGMZrMR9zSfNJl4JXHiqP64zJCzak=
|
||||
github.com/hashicorp/terraform v0.12.0-alpha4.0.20190417210818-177a7afb781f/go.mod h1:A3NsI7WT87OMgpcD15cu6dK2YNpihchZp5fxUf8EHBg=
|
||||
github.com/hashicorp/terraform v0.12.0-rc1.0.20190517222244-f19903073985 h1:LEATwRYarkYnFy8IPEl+NorbK0AuBS+5uI+Aiy8o038=
|
||||
github.com/hashicorp/terraform v0.12.0-rc1.0.20190517222244-f19903073985/go.mod h1:Ke0ig9gGZ8rhV6OddAhBYt5nXmpvXsuNQQ8w9qYBZfU=
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20190327195015-8022a2663a70 h1:oZm5nE11yhzsTRz/YrUyDMSvixePqjoZihwn8ipuOYI=
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20190327195015-8022a2663a70/go.mod h1:ItvqtvbC3K23FFET62ZwnkwtpbKZm8t8eMcWjmVVjD8=
|
||||
github.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
|
||||
|
@ -362,9 +364,9 @@ github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q
|
|||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs=
|
||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/zclconf/go-cty v0.0.0-20181129180422-88fbe721e0f8/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v0.0.0-20190124225737-a385d646c1e9/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v0.0.0-20190320224746-fd76348b9329 h1:ne520NlvoncW5zfBGkmP4EJhyd6ruSaSyhzobv0Vz9w=
|
||||
github.com/zclconf/go-cty v0.0.0-20190320224746-fd76348b9329/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v0.0.0-20190426224007-b18a157db9e2/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v0.0.0-20190516203816-4fecf87372ec h1:MSeYjmyjucsFbecMTxg63ASg23lcSARP/kr9sClTFfk=
|
||||
github.com/zclconf/go-cty v0.0.0-20190516203816-4fecf87372ec/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
|
@ -382,6 +384,8 @@ golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -399,6 +403,9 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
|||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab h1:9RfW3ktsOZxgo9YNbBAjq1FWzc/igwEcUzZz8IXgSbk=
|
||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -413,6 +420,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FY
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -424,14 +433,20 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -469,7 +484,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
|||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -478,7 +492,5 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
labix.org/v2/mgo v0.0.0-20140701140051-000000000287/go.mod h1:Lg7AYkt1uXJoR9oeSZ3W/8IXLdvOfIITgZnommstyz4=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2017 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Annotate prepends msg to the error message in err, attempting
|
||||
// to preserve other information in err, like an error code.
|
||||
//
|
||||
// Annotate panics if err is nil.
|
||||
//
|
||||
// Annotate knows about these error types:
|
||||
// - "google.golang.org/grpc/status".Status
|
||||
// - "google.golang.org/api/googleapi".Error
|
||||
// If the error is not one of these types, Annotate behaves
|
||||
// like
|
||||
// fmt.Errorf("%s: %v", msg, err)
|
||||
func Annotate(err error, msg string) error {
|
||||
if err == nil {
|
||||
panic("Annotate called with nil")
|
||||
}
|
||||
if s, ok := status.FromError(err); ok {
|
||||
p := s.Proto()
|
||||
p.Message = msg + ": " + p.Message
|
||||
return status.ErrorProto(p)
|
||||
}
|
||||
if g, ok := err.(*googleapi.Error); ok {
|
||||
g.Message = msg + ": " + g.Message
|
||||
return g
|
||||
}
|
||||
return fmt.Errorf("%s: %v", msg, err)
|
||||
}
|
||||
|
||||
// Annotatef uses format and args to format a string, then calls Annotate.
|
||||
func Annotatef(err error, format string, args ...interface{}) error {
|
||||
return Annotate(err, fmt.Sprintf(format, args...))
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2016 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
gax "github.com/googleapis/gax-go/v2"
|
||||
)
|
||||
|
||||
// Retry calls the supplied function f repeatedly according to the provided
|
||||
// backoff parameters. It returns when one of the following occurs:
|
||||
// When f's first return value is true, Retry immediately returns with f's second
|
||||
// return value.
|
||||
// When the provided context is done, Retry returns with an error that
|
||||
// includes both ctx.Error() and the last error returned by f.
|
||||
func Retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error)) error {
|
||||
return retry(ctx, bo, f, gax.Sleep)
|
||||
}
|
||||
|
||||
func retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error),
|
||||
sleep func(context.Context, time.Duration) error) error {
|
||||
var lastErr error
|
||||
for {
|
||||
stop, err := f()
|
||||
if stop {
|
||||
return err
|
||||
}
|
||||
// Remember the last "real" error from f.
|
||||
if err != nil && err != context.Canceled && err != context.DeadlineExceeded {
|
||||
lastErr = err
|
||||
}
|
||||
p := bo.Pause()
|
||||
if cerr := sleep(ctx, p); cerr != nil {
|
||||
if lastErr != nil {
|
||||
return Annotatef(lastErr, "retry failed with %v; last error", cerr)
|
||||
}
|
||||
return cerr
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/genproto/googleapis/rpc/code"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// StartSpan adds a span to the trace with the given name.
|
||||
func StartSpan(ctx context.Context, name string) context.Context {
|
||||
ctx, _ = trace.StartSpan(ctx, name)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// EndSpan ends a span with the given error.
|
||||
func EndSpan(ctx context.Context, err error) {
|
||||
span := trace.FromContext(ctx)
|
||||
if err != nil {
|
||||
span.SetStatus(toStatus(err))
|
||||
}
|
||||
span.End()
|
||||
}
|
||||
|
||||
// ToStatus interrogates an error and converts it to an appropriate
|
||||
// OpenCensus status.
|
||||
func toStatus(err error) trace.Status {
|
||||
if err2, ok := err.(*googleapi.Error); ok {
|
||||
return trace.Status{Code: httpStatusCodeToOCCode(err2.Code), Message: err2.Message}
|
||||
} else if s, ok := status.FromError(err); ok {
|
||||
return trace.Status{Code: int32(s.Code()), Message: s.Message()}
|
||||
} else {
|
||||
return trace.Status{Code: int32(code.Code_UNKNOWN), Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (deklerk): switch to using OpenCensus function when it becomes available.
|
||||
// Reference: https://github.com/googleapis/googleapis/blob/26b634d2724ac5dd30ae0b0cbfb01f07f2e4050e/google/rpc/code.proto
|
||||
func httpStatusCodeToOCCode(httpStatusCode int) int32 {
|
||||
switch httpStatusCode {
|
||||
case 200:
|
||||
return int32(code.Code_OK)
|
||||
case 499:
|
||||
return int32(code.Code_CANCELLED)
|
||||
case 500:
|
||||
return int32(code.Code_UNKNOWN) // Could also be Code_INTERNAL, Code_DATA_LOSS
|
||||
case 400:
|
||||
return int32(code.Code_INVALID_ARGUMENT) // Could also be Code_OUT_OF_RANGE
|
||||
case 504:
|
||||
return int32(code.Code_DEADLINE_EXCEEDED)
|
||||
case 404:
|
||||
return int32(code.Code_NOT_FOUND)
|
||||
case 409:
|
||||
return int32(code.Code_ALREADY_EXISTS) // Could also be Code_ABORTED
|
||||
case 403:
|
||||
return int32(code.Code_PERMISSION_DENIED)
|
||||
case 401:
|
||||
return int32(code.Code_UNAUTHENTICATED)
|
||||
case 429:
|
||||
return int32(code.Code_RESOURCE_EXHAUSTED)
|
||||
case 501:
|
||||
return int32(code.Code_UNIMPLEMENTED)
|
||||
case 503:
|
||||
return int32(code.Code_UNAVAILABLE)
|
||||
default:
|
||||
return int32(code.Code_UNKNOWN)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
today=$(date +%Y%m%d)
|
||||
|
||||
sed -i -r -e 's/const Repo = "([0-9]{8})"/const Repo = "'$today'"/' $GOFILE
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2016 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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.
|
||||
|
||||
//go:generate ./update_version.sh
|
||||
|
||||
// Package version contains version information for Google Cloud Client
|
||||
// Libraries for Go, as reported in request headers.
|
||||
package version
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Repo is the current version of the client libraries in this
|
||||
// repo. It should be a date in YYYYMMDD format.
|
||||
const Repo = "20180226"
|
||||
|
||||
// Go returns the Go runtime version. The returned string
|
||||
// has no whitespace.
|
||||
func Go() string {
|
||||
return goVersion
|
||||
}
|
||||
|
||||
var goVersion = goVer(runtime.Version())
|
||||
|
||||
const develPrefix = "devel +"
|
||||
|
||||
func goVer(s string) string {
|
||||
if strings.HasPrefix(s, develPrefix) {
|
||||
s = s[len(develPrefix):]
|
||||
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
||||
s = s[:p]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, "go1") {
|
||||
s = s[2:]
|
||||
var prerelease string
|
||||
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
|
||||
s, prerelease = s[:p], s[p:]
|
||||
}
|
||||
if strings.HasSuffix(s, ".") {
|
||||
s += "0"
|
||||
} else if strings.Count(s, ".") < 2 {
|
||||
s += ".0"
|
||||
}
|
||||
if prerelease != "" {
|
||||
s += "-" + prerelease
|
||||
}
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func notSemverRune(r rune) bool {
|
||||
return !strings.ContainsRune("0123456789.", r)
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"cloud.google.com/go/internal/trace"
|
||||
"google.golang.org/api/googleapi"
|
||||
raw "google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
// ACLRole is the level of access to grant.
|
||||
type ACLRole string
|
||||
|
||||
const (
|
||||
RoleOwner ACLRole = "OWNER"
|
||||
RoleReader ACLRole = "READER"
|
||||
RoleWriter ACLRole = "WRITER"
|
||||
)
|
||||
|
||||
// ACLEntity refers to a user or group.
|
||||
// They are sometimes referred to as grantees.
|
||||
//
|
||||
// It could be in the form of:
|
||||
// "user-<userId>", "user-<email>", "group-<groupId>", "group-<email>",
|
||||
// "domain-<domain>" and "project-team-<projectId>".
|
||||
//
|
||||
// Or one of the predefined constants: AllUsers, AllAuthenticatedUsers.
|
||||
type ACLEntity string
|
||||
|
||||
const (
|
||||
AllUsers ACLEntity = "allUsers"
|
||||
AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers"
|
||||
)
|
||||
|
||||
// ACLRule represents a grant for a role to an entity (user, group or team) for a
|
||||
// Google Cloud Storage object or bucket.
|
||||
type ACLRule struct {
|
||||
Entity ACLEntity
|
||||
EntityID string
|
||||
Role ACLRole
|
||||
Domain string
|
||||
Email string
|
||||
ProjectTeam *ProjectTeam
|
||||
}
|
||||
|
||||
// ProjectTeam is the project team associated with the entity, if any.
|
||||
type ProjectTeam struct {
|
||||
ProjectNumber string
|
||||
Team string
|
||||
}
|
||||
|
||||
// ACLHandle provides operations on an access control list for a Google Cloud Storage bucket or object.
|
||||
type ACLHandle struct {
|
||||
c *Client
|
||||
bucket string
|
||||
object string
|
||||
isDefault bool
|
||||
userProject string // for requester-pays buckets
|
||||
}
|
||||
|
||||
// Delete permanently deletes the ACL entry for the given entity.
|
||||
func (a *ACLHandle) Delete(ctx context.Context, entity ACLEntity) (err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Delete")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if a.object != "" {
|
||||
return a.objectDelete(ctx, entity)
|
||||
}
|
||||
if a.isDefault {
|
||||
return a.bucketDefaultDelete(ctx, entity)
|
||||
}
|
||||
return a.bucketDelete(ctx, entity)
|
||||
}
|
||||
|
||||
// Set sets the role for the given entity.
|
||||
func (a *ACLHandle) Set(ctx context.Context, entity ACLEntity, role ACLRole) (err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Set")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if a.object != "" {
|
||||
return a.objectSet(ctx, entity, role, false)
|
||||
}
|
||||
if a.isDefault {
|
||||
return a.objectSet(ctx, entity, role, true)
|
||||
}
|
||||
return a.bucketSet(ctx, entity, role)
|
||||
}
|
||||
|
||||
// List retrieves ACL entries.
|
||||
func (a *ACLHandle) List(ctx context.Context) (rules []ACLRule, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.List")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if a.object != "" {
|
||||
return a.objectList(ctx)
|
||||
}
|
||||
if a.isDefault {
|
||||
return a.bucketDefaultList(ctx)
|
||||
}
|
||||
return a.bucketList(ctx)
|
||||
}
|
||||
|
||||
func (a *ACLHandle) bucketDefaultList(ctx context.Context) ([]ACLRule, error) {
|
||||
var acls *raw.ObjectAccessControls
|
||||
var err error
|
||||
err = runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.DefaultObjectAccessControls.List(a.bucket)
|
||||
a.configureCall(ctx, req)
|
||||
acls, err = req.Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toObjectACLRules(acls.Items), nil
|
||||
}
|
||||
|
||||
func (a *ACLHandle) bucketDefaultDelete(ctx context.Context, entity ACLEntity) error {
|
||||
return runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity))
|
||||
a.configureCall(ctx, req)
|
||||
return req.Do()
|
||||
})
|
||||
}
|
||||
|
||||
func (a *ACLHandle) bucketList(ctx context.Context) ([]ACLRule, error) {
|
||||
var acls *raw.BucketAccessControls
|
||||
var err error
|
||||
err = runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.BucketAccessControls.List(a.bucket)
|
||||
a.configureCall(ctx, req)
|
||||
acls, err = req.Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toBucketACLRules(acls.Items), nil
|
||||
}
|
||||
|
||||
func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRole) error {
|
||||
acl := &raw.BucketAccessControl{
|
||||
Bucket: a.bucket,
|
||||
Entity: string(entity),
|
||||
Role: string(role),
|
||||
}
|
||||
err := runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl)
|
||||
a.configureCall(ctx, req)
|
||||
_, err := req.Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ACLHandle) bucketDelete(ctx context.Context, entity ACLEntity) error {
|
||||
return runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity))
|
||||
a.configureCall(ctx, req)
|
||||
return req.Do()
|
||||
})
|
||||
}
|
||||
|
||||
func (a *ACLHandle) objectList(ctx context.Context) ([]ACLRule, error) {
|
||||
var acls *raw.ObjectAccessControls
|
||||
var err error
|
||||
err = runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.ObjectAccessControls.List(a.bucket, a.object)
|
||||
a.configureCall(ctx, req)
|
||||
acls, err = req.Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toObjectACLRules(acls.Items), nil
|
||||
}
|
||||
|
||||
func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRole, isBucketDefault bool) error {
|
||||
type setRequest interface {
|
||||
Do(opts ...googleapi.CallOption) (*raw.ObjectAccessControl, error)
|
||||
Header() http.Header
|
||||
}
|
||||
|
||||
acl := &raw.ObjectAccessControl{
|
||||
Bucket: a.bucket,
|
||||
Entity: string(entity),
|
||||
Role: string(role),
|
||||
}
|
||||
var req setRequest
|
||||
if isBucketDefault {
|
||||
req = a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl)
|
||||
} else {
|
||||
req = a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl)
|
||||
}
|
||||
a.configureCall(ctx, req)
|
||||
return runWithRetry(ctx, func() error {
|
||||
_, err := req.Do()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (a *ACLHandle) objectDelete(ctx context.Context, entity ACLEntity) error {
|
||||
return runWithRetry(ctx, func() error {
|
||||
req := a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity))
|
||||
a.configureCall(ctx, req)
|
||||
return req.Do()
|
||||
})
|
||||
}
|
||||
|
||||
func (a *ACLHandle) configureCall(ctx context.Context, call interface{ Header() http.Header }) {
|
||||
vc := reflect.ValueOf(call)
|
||||
vc.MethodByName("Context").Call([]reflect.Value{reflect.ValueOf(ctx)})
|
||||
if a.userProject != "" {
|
||||
vc.MethodByName("UserProject").Call([]reflect.Value{reflect.ValueOf(a.userProject)})
|
||||
}
|
||||
setClientHeader(call.Header())
|
||||
}
|
||||
|
||||
func toObjectACLRules(items []*raw.ObjectAccessControl) []ACLRule {
|
||||
var rs []ACLRule
|
||||
for _, item := range items {
|
||||
rs = append(rs, toObjectACLRule(item))
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
func toBucketACLRules(items []*raw.BucketAccessControl) []ACLRule {
|
||||
var rs []ACLRule
|
||||
for _, item := range items {
|
||||
rs = append(rs, toBucketACLRule(item))
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
func toObjectACLRule(a *raw.ObjectAccessControl) ACLRule {
|
||||
return ACLRule{
|
||||
Entity: ACLEntity(a.Entity),
|
||||
EntityID: a.EntityId,
|
||||
Role: ACLRole(a.Role),
|
||||
Domain: a.Domain,
|
||||
Email: a.Email,
|
||||
ProjectTeam: toObjectProjectTeam(a.ProjectTeam),
|
||||
}
|
||||
}
|
||||
|
||||
func toBucketACLRule(a *raw.BucketAccessControl) ACLRule {
|
||||
return ACLRule{
|
||||
Entity: ACLEntity(a.Entity),
|
||||
EntityID: a.EntityId,
|
||||
Role: ACLRole(a.Role),
|
||||
Domain: a.Domain,
|
||||
Email: a.Email,
|
||||
ProjectTeam: toBucketProjectTeam(a.ProjectTeam),
|
||||
}
|
||||
}
|
||||
|
||||
func toRawObjectACL(rules []ACLRule) []*raw.ObjectAccessControl {
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
r := make([]*raw.ObjectAccessControl, 0, len(rules))
|
||||
for _, rule := range rules {
|
||||
r = append(r, rule.toRawObjectAccessControl("")) // bucket name unnecessary
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func toRawBucketACL(rules []ACLRule) []*raw.BucketAccessControl {
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
r := make([]*raw.BucketAccessControl, 0, len(rules))
|
||||
for _, rule := range rules {
|
||||
r = append(r, rule.toRawBucketAccessControl("")) // bucket name unnecessary
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ACLRule) toRawBucketAccessControl(bucket string) *raw.BucketAccessControl {
|
||||
return &raw.BucketAccessControl{
|
||||
Bucket: bucket,
|
||||
Entity: string(r.Entity),
|
||||
Role: string(r.Role),
|
||||
// The other fields are not settable.
|
||||
}
|
||||
}
|
||||
|
||||
func (r ACLRule) toRawObjectAccessControl(bucket string) *raw.ObjectAccessControl {
|
||||
return &raw.ObjectAccessControl{
|
||||
Bucket: bucket,
|
||||
Entity: string(r.Entity),
|
||||
Role: string(r.Role),
|
||||
// The other fields are not settable.
|
||||
}
|
||||
}
|
||||
|
||||
func toBucketProjectTeam(p *raw.BucketAccessControlProjectTeam) *ProjectTeam {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return &ProjectTeam{
|
||||
ProjectNumber: p.ProjectNumber,
|
||||
Team: p.Team,
|
||||
}
|
||||
}
|
||||
|
||||
func toObjectProjectTeam(p *raw.ObjectAccessControlProjectTeam) *ProjectTeam {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return &ProjectTeam{
|
||||
ProjectNumber: p.ProjectNumber,
|
||||
Team: p.Team,
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,228 @@
|
|||
// Copyright 2016 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"cloud.google.com/go/internal/trace"
|
||||
raw "google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
// CopierFrom creates a Copier that can copy src to dst.
|
||||
// You can immediately call Run on the returned Copier, or
|
||||
// you can configure it first.
|
||||
//
|
||||
// For Requester Pays buckets, the user project of dst is billed, unless it is empty,
|
||||
// in which case the user project of src is billed.
|
||||
func (dst *ObjectHandle) CopierFrom(src *ObjectHandle) *Copier {
|
||||
return &Copier{dst: dst, src: src}
|
||||
}
|
||||
|
||||
// A Copier copies a source object to a destination.
|
||||
type Copier struct {
|
||||
// ObjectAttrs are optional attributes to set on the destination object.
|
||||
// Any attributes must be initialized before any calls on the Copier. Nil
|
||||
// or zero-valued attributes are ignored.
|
||||
ObjectAttrs
|
||||
|
||||
// RewriteToken can be set before calling Run to resume a copy
|
||||
// operation. After Run returns a non-nil error, RewriteToken will
|
||||
// have been updated to contain the value needed to resume the copy.
|
||||
RewriteToken string
|
||||
|
||||
// ProgressFunc can be used to monitor the progress of a multi-RPC copy
|
||||
// operation. If ProgressFunc is not nil and copying requires multiple
|
||||
// calls to the underlying service (see
|
||||
// https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite), then
|
||||
// ProgressFunc will be invoked after each call with the number of bytes of
|
||||
// content copied so far and the total size in bytes of the source object.
|
||||
//
|
||||
// ProgressFunc is intended to make upload progress available to the
|
||||
// application. For example, the implementation of ProgressFunc may update
|
||||
// a progress bar in the application's UI, or log the result of
|
||||
// float64(copiedBytes)/float64(totalBytes).
|
||||
//
|
||||
// ProgressFunc should return quickly without blocking.
|
||||
ProgressFunc func(copiedBytes, totalBytes uint64)
|
||||
|
||||
// The Cloud KMS key, in the form projects/P/locations/L/keyRings/R/cryptoKeys/K,
|
||||
// that will be used to encrypt the object. Overrides the object's KMSKeyName, if
|
||||
// any.
|
||||
//
|
||||
// Providing both a DestinationKMSKeyName and a customer-supplied encryption key
|
||||
// (via ObjectHandle.Key) on the destination object will result in an error when
|
||||
// Run is called.
|
||||
DestinationKMSKeyName string
|
||||
|
||||
dst, src *ObjectHandle
|
||||
}
|
||||
|
||||
// Run performs the copy.
|
||||
func (c *Copier) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Copier.Run")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if err := c.src.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.dst.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.DestinationKMSKeyName != "" && c.dst.encryptionKey != nil {
|
||||
return nil, errors.New("storage: cannot use DestinationKMSKeyName with a customer-supplied encryption key")
|
||||
}
|
||||
// Convert destination attributes to raw form, omitting the bucket.
|
||||
// If the bucket is included but name or content-type aren't, the service
|
||||
// returns a 400 with "Required" as the only message. Omitting the bucket
|
||||
// does not cause any problems.
|
||||
rawObject := c.ObjectAttrs.toRawObject("")
|
||||
for {
|
||||
res, err := c.callRewrite(ctx, rawObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.ProgressFunc != nil {
|
||||
c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize))
|
||||
}
|
||||
if res.Done { // Finished successfully.
|
||||
return newObject(res.Resource), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Copier) callRewrite(ctx context.Context, rawObj *raw.Object) (*raw.RewriteResponse, error) {
|
||||
call := c.dst.c.raw.Objects.Rewrite(c.src.bucket, c.src.object, c.dst.bucket, c.dst.object, rawObj)
|
||||
|
||||
call.Context(ctx).Projection("full")
|
||||
if c.RewriteToken != "" {
|
||||
call.RewriteToken(c.RewriteToken)
|
||||
}
|
||||
if c.DestinationKMSKeyName != "" {
|
||||
call.DestinationKmsKeyName(c.DestinationKMSKeyName)
|
||||
}
|
||||
if c.PredefinedACL != "" {
|
||||
call.DestinationPredefinedAcl(c.PredefinedACL)
|
||||
}
|
||||
if err := applyConds("Copy destination", c.dst.gen, c.dst.conds, call); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.dst.userProject != "" {
|
||||
call.UserProject(c.dst.userProject)
|
||||
} else if c.src.userProject != "" {
|
||||
call.UserProject(c.src.userProject)
|
||||
}
|
||||
if err := applySourceConds(c.src.gen, c.src.conds, call); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setEncryptionHeaders(call.Header(), c.src.encryptionKey, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res *raw.RewriteResponse
|
||||
var err error
|
||||
setClientHeader(call.Header())
|
||||
err = runWithRetry(ctx, func() error { res, err = call.Do(); return err })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.RewriteToken = res.RewriteToken
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ComposerFrom creates a Composer that can compose srcs into dst.
|
||||
// You can immediately call Run on the returned Composer, or you can
|
||||
// configure it first.
|
||||
//
|
||||
// The encryption key for the destination object will be used to decrypt all
|
||||
// source objects and encrypt the destination object. It is an error
|
||||
// to specify an encryption key for any of the source objects.
|
||||
func (dst *ObjectHandle) ComposerFrom(srcs ...*ObjectHandle) *Composer {
|
||||
return &Composer{dst: dst, srcs: srcs}
|
||||
}
|
||||
|
||||
// A Composer composes source objects into a destination object.
|
||||
//
|
||||
// For Requester Pays buckets, the user project of dst is billed.
|
||||
type Composer struct {
|
||||
// ObjectAttrs are optional attributes to set on the destination object.
|
||||
// Any attributes must be initialized before any calls on the Composer. Nil
|
||||
// or zero-valued attributes are ignored.
|
||||
ObjectAttrs
|
||||
|
||||
dst *ObjectHandle
|
||||
srcs []*ObjectHandle
|
||||
}
|
||||
|
||||
// Run performs the compose operation.
|
||||
func (c *Composer) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Composer.Run")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if err := c.dst.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(c.srcs) == 0 {
|
||||
return nil, errors.New("storage: at least one source object must be specified")
|
||||
}
|
||||
|
||||
req := &raw.ComposeRequest{}
|
||||
// Compose requires a non-empty Destination, so we always set it,
|
||||
// even if the caller-provided ObjectAttrs is the zero value.
|
||||
req.Destination = c.ObjectAttrs.toRawObject(c.dst.bucket)
|
||||
for _, src := range c.srcs {
|
||||
if err := src.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if src.bucket != c.dst.bucket {
|
||||
return nil, fmt.Errorf("storage: all source objects must be in bucket %q, found %q", c.dst.bucket, src.bucket)
|
||||
}
|
||||
if src.encryptionKey != nil {
|
||||
return nil, fmt.Errorf("storage: compose source %s.%s must not have encryption key", src.bucket, src.object)
|
||||
}
|
||||
srcObj := &raw.ComposeRequestSourceObjects{
|
||||
Name: src.object,
|
||||
}
|
||||
if err := applyConds("ComposeFrom source", src.gen, src.conds, composeSourceObj{srcObj}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.SourceObjects = append(req.SourceObjects, srcObj)
|
||||
}
|
||||
|
||||
call := c.dst.c.raw.Objects.Compose(c.dst.bucket, c.dst.object, req).Context(ctx)
|
||||
if err := applyConds("ComposeFrom destination", c.dst.gen, c.dst.conds, call); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.dst.userProject != "" {
|
||||
call.UserProject(c.dst.userProject)
|
||||
}
|
||||
if c.PredefinedACL != "" {
|
||||
call.DestinationPredefinedAcl(c.PredefinedACL)
|
||||
}
|
||||
if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var obj *raw.Object
|
||||
setClientHeader(call.Header())
|
||||
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newObject(obj), nil
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
// Copyright 2016 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage provides an easy way to work with Google Cloud Storage.
|
||||
Google Cloud Storage stores data in named objects, which are grouped into buckets.
|
||||
|
||||
More information about Google Cloud Storage is available at
|
||||
https://cloud.google.com/storage/docs.
|
||||
|
||||
See https://godoc.org/cloud.google.com/go for authentication, timeouts,
|
||||
connection pooling and similar aspects of this package.
|
||||
|
||||
All of the methods of this package use exponential backoff to retry calls that fail
|
||||
with certain errors, as described in
|
||||
https://cloud.google.com/storage/docs/exponential-backoff. Retrying continues
|
||||
indefinitely unless the controlling context is canceled or the client is closed. See
|
||||
context.WithTimeout and context.WithCancel.
|
||||
|
||||
|
||||
Creating a Client
|
||||
|
||||
To start working with this package, create a client:
|
||||
|
||||
ctx := context.Background()
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
|
||||
The client will use your default application credentials.
|
||||
|
||||
If you only wish to access public data, you can create
|
||||
an unauthenticated client with
|
||||
|
||||
client, err := storage.NewClient(ctx, option.WithoutAuthentication())
|
||||
|
||||
Buckets
|
||||
|
||||
A Google Cloud Storage bucket is a collection of objects. To work with a
|
||||
bucket, make a bucket handle:
|
||||
|
||||
bkt := client.Bucket(bucketName)
|
||||
|
||||
A handle is a reference to a bucket. You can have a handle even if the
|
||||
bucket doesn't exist yet. To create a bucket in Google Cloud Storage,
|
||||
call Create on the handle:
|
||||
|
||||
if err := bkt.Create(ctx, projectID, nil); err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
|
||||
Note that although buckets are associated with projects, bucket names are
|
||||
global across all projects.
|
||||
|
||||
Each bucket has associated metadata, represented in this package by
|
||||
BucketAttrs. The third argument to BucketHandle.Create allows you to set
|
||||
the initial BucketAttrs of a bucket. To retrieve a bucket's attributes, use
|
||||
Attrs:
|
||||
|
||||
attrs, err := bkt.Attrs(ctx)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
fmt.Printf("bucket %s, created at %s, is located in %s with storage class %s\n",
|
||||
attrs.Name, attrs.Created, attrs.Location, attrs.StorageClass)
|
||||
|
||||
Objects
|
||||
|
||||
An object holds arbitrary data as a sequence of bytes, like a file. You
|
||||
refer to objects using a handle, just as with buckets, but unlike buckets
|
||||
you don't explicitly create an object. Instead, the first time you write
|
||||
to an object it will be created. You can use the standard Go io.Reader
|
||||
and io.Writer interfaces to read and write object data:
|
||||
|
||||
obj := bkt.Object("data")
|
||||
// Write something to obj.
|
||||
// w implements io.Writer.
|
||||
w := obj.NewWriter(ctx)
|
||||
// Write some text to obj. This will either create the object or overwrite whatever is there already.
|
||||
if _, err := fmt.Fprintf(w, "This object contains text.\n"); err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
// Close, just like writing a file.
|
||||
if err := w.Close(); err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
|
||||
// Read it back.
|
||||
r, err := obj.NewReader(ctx)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
defer r.Close()
|
||||
if _, err := io.Copy(os.Stdout, r); err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
// Prints "This object contains text."
|
||||
|
||||
Objects also have attributes, which you can fetch with Attrs:
|
||||
|
||||
objAttrs, err := obj.Attrs(ctx)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
fmt.Printf("object %s has size %d and can be read using %s\n",
|
||||
objAttrs.Name, objAttrs.Size, objAttrs.MediaLink)
|
||||
|
||||
ACLs
|
||||
|
||||
Both objects and buckets have ACLs (Access Control Lists). An ACL is a list of
|
||||
ACLRules, each of which specifies the role of a user, group or project. ACLs
|
||||
are suitable for fine-grained control, but you may prefer using IAM to control
|
||||
access at the project level (see
|
||||
https://cloud.google.com/storage/docs/access-control/iam).
|
||||
|
||||
To list the ACLs of a bucket or object, obtain an ACLHandle and call its List method:
|
||||
|
||||
acls, err := obj.ACL().List(ctx)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
for _, rule := range acls {
|
||||
fmt.Printf("%s has role %s\n", rule.Entity, rule.Role)
|
||||
}
|
||||
|
||||
You can also set and delete ACLs.
|
||||
|
||||
Conditions
|
||||
|
||||
Every object has a generation and a metageneration. The generation changes
|
||||
whenever the content changes, and the metageneration changes whenever the
|
||||
metadata changes. Conditions let you check these values before an operation;
|
||||
the operation only executes if the conditions match. You can use conditions to
|
||||
prevent race conditions in read-modify-write operations.
|
||||
|
||||
For example, say you've read an object's metadata into objAttrs. Now
|
||||
you want to write to that object, but only if its contents haven't changed
|
||||
since you read it. Here is how to express that:
|
||||
|
||||
w = obj.If(storage.Conditions{GenerationMatch: objAttrs.Generation}).NewWriter(ctx)
|
||||
// Proceed with writing as above.
|
||||
|
||||
Signed URLs
|
||||
|
||||
You can obtain a URL that lets anyone read or write an object for a limited time.
|
||||
You don't need to create a client to do this. See the documentation of
|
||||
SignedURL for details.
|
||||
|
||||
url, err := storage.SignedURL(bucketName, "shared-object", opts)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
fmt.Println(url)
|
||||
|
||||
Errors
|
||||
|
||||
Errors returned by this client are often of the type [`googleapi.Error`](https://godoc.org/google.golang.org/api/googleapi#Error).
|
||||
These errors can be introspected for more information by type asserting to the richer `googleapi.Error` type. For example:
|
||||
|
||||
if e, ok := err.(*googleapi.Error); ok {
|
||||
if e.Code == 409 { ... }
|
||||
}
|
||||
*/
|
||||
package storage // import "cloud.google.com/go/storage"
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2017 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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.
|
||||
|
||||
// +build go1.10
|
||||
|
||||
package storage
|
||||
|
||||
import "google.golang.org/api/googleapi"
|
||||
|
||||
func shouldRetry(err error) bool {
|
||||
switch e := err.(type) {
|
||||
case *googleapi.Error:
|
||||
// Retry on 429 and 5xx, according to
|
||||
// https://cloud.google.com/storage/docs/exponential-backoff.
|
||||
return e.Code == 429 || (e.Code >= 500 && e.Code < 600)
|
||||
case interface{ Temporary() bool }:
|
||||
return e.Temporary()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright 2017 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cloud.google.com/go/iam"
|
||||
"cloud.google.com/go/internal/trace"
|
||||
raw "google.golang.org/api/storage/v1"
|
||||
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||
)
|
||||
|
||||
// IAM provides access to IAM access control for the bucket.
|
||||
func (b *BucketHandle) IAM() *iam.Handle {
|
||||
return iam.InternalNewHandleClient(&iamClient{
|
||||
raw: b.c.raw,
|
||||
userProject: b.userProject,
|
||||
}, b.name)
|
||||
}
|
||||
|
||||
// iamClient implements the iam.client interface.
|
||||
type iamClient struct {
|
||||
raw *raw.Service
|
||||
userProject string
|
||||
}
|
||||
|
||||
func (c *iamClient) Get(ctx context.Context, resource string) (p *iampb.Policy, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.IAM.Get")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
call := c.raw.Buckets.GetIamPolicy(resource)
|
||||
setClientHeader(call.Header())
|
||||
if c.userProject != "" {
|
||||
call.UserProject(c.userProject)
|
||||
}
|
||||
var rp *raw.Policy
|
||||
err = runWithRetry(ctx, func() error {
|
||||
rp, err = call.Context(ctx).Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iamFromStoragePolicy(rp), nil
|
||||
}
|
||||
|
||||
func (c *iamClient) Set(ctx context.Context, resource string, p *iampb.Policy) (err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.IAM.Set")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
rp := iamToStoragePolicy(p)
|
||||
call := c.raw.Buckets.SetIamPolicy(resource, rp)
|
||||
setClientHeader(call.Header())
|
||||
if c.userProject != "" {
|
||||
call.UserProject(c.userProject)
|
||||
}
|
||||
return runWithRetry(ctx, func() error {
|
||||
_, err := call.Context(ctx).Do()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (c *iamClient) Test(ctx context.Context, resource string, perms []string) (permissions []string, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.IAM.Test")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
call := c.raw.Buckets.TestIamPermissions(resource, perms)
|
||||
setClientHeader(call.Header())
|
||||
if c.userProject != "" {
|
||||
call.UserProject(c.userProject)
|
||||
}
|
||||
var res *raw.TestIamPermissionsResponse
|
||||
err = runWithRetry(ctx, func() error {
|
||||
res, err = call.Context(ctx).Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.Permissions, nil
|
||||
}
|
||||
|
||||
func iamToStoragePolicy(ip *iampb.Policy) *raw.Policy {
|
||||
return &raw.Policy{
|
||||
Bindings: iamToStorageBindings(ip.Bindings),
|
||||
Etag: string(ip.Etag),
|
||||
}
|
||||
}
|
||||
|
||||
func iamToStorageBindings(ibs []*iampb.Binding) []*raw.PolicyBindings {
|
||||
var rbs []*raw.PolicyBindings
|
||||
for _, ib := range ibs {
|
||||
rbs = append(rbs, &raw.PolicyBindings{
|
||||
Role: ib.Role,
|
||||
Members: ib.Members,
|
||||
})
|
||||
}
|
||||
return rbs
|
||||
}
|
||||
|
||||
func iamFromStoragePolicy(rp *raw.Policy) *iampb.Policy {
|
||||
return &iampb.Policy{
|
||||
Bindings: iamFromStorageBindings(rp.Bindings),
|
||||
Etag: []byte(rp.Etag),
|
||||
}
|
||||
}
|
||||
|
||||
func iamFromStorageBindings(rbs []*raw.PolicyBindings) []*iampb.Binding {
|
||||
var ibs []*iampb.Binding
|
||||
for _, rb := range rbs {
|
||||
ibs = append(ibs, &iampb.Binding{
|
||||
Role: rb.Role,
|
||||
Members: rb.Members,
|
||||
})
|
||||
}
|
||||
return ibs
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cloud.google.com/go/internal"
|
||||
gax "github.com/googleapis/gax-go/v2"
|
||||
)
|
||||
|
||||
// runWithRetry calls the function until it returns nil or a non-retryable error, or
|
||||
// the context is done.
|
||||
func runWithRetry(ctx context.Context, call func() error) error {
|
||||
return internal.Retry(ctx, gax.Backoff{}, func() (stop bool, err error) {
|
||||
err = call()
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if shouldRetry(err) {
|
||||
return false, nil
|
||||
}
|
||||
return true, err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2017 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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.
|
||||
|
||||
// +build !go1.10
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
func shouldRetry(err error) bool {
|
||||
switch e := err.(type) {
|
||||
case *googleapi.Error:
|
||||
// Retry on 429 and 5xx, according to
|
||||
// https://cloud.google.com/storage/docs/exponential-backoff.
|
||||
return e.Code == 429 || (e.Code >= 500 && e.Code < 600)
|
||||
case *url.Error:
|
||||
// Retry on REFUSED_STREAM.
|
||||
// Unfortunately the error type is unexported, so we resort to string
|
||||
// matching.
|
||||
return strings.Contains(e.Error(), "REFUSED_STREAM")
|
||||
case interface{ Temporary() bool }:
|
||||
return e.Temporary()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
// Copyright 2017 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"cloud.google.com/go/internal/trace"
|
||||
raw "google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
// A Notification describes how to send Cloud PubSub messages when certain
|
||||
// events occur in a bucket.
|
||||
type Notification struct {
|
||||
//The ID of the notification.
|
||||
ID string
|
||||
|
||||
// The ID of the topic to which this subscription publishes.
|
||||
TopicID string
|
||||
|
||||
// The ID of the project to which the topic belongs.
|
||||
TopicProjectID string
|
||||
|
||||
// Only send notifications about listed event types. If empty, send notifications
|
||||
// for all event types.
|
||||
// See https://cloud.google.com/storage/docs/pubsub-notifications#events.
|
||||
EventTypes []string
|
||||
|
||||
// If present, only apply this notification configuration to object names that
|
||||
// begin with this prefix.
|
||||
ObjectNamePrefix string
|
||||
|
||||
// An optional list of additional attributes to attach to each Cloud PubSub
|
||||
// message published for this notification subscription.
|
||||
CustomAttributes map[string]string
|
||||
|
||||
// The contents of the message payload.
|
||||
// See https://cloud.google.com/storage/docs/pubsub-notifications#payload.
|
||||
PayloadFormat string
|
||||
}
|
||||
|
||||
// Values for Notification.PayloadFormat.
|
||||
const (
|
||||
// Send no payload with notification messages.
|
||||
NoPayload = "NONE"
|
||||
|
||||
// Send object metadata as JSON with notification messages.
|
||||
JSONPayload = "JSON_API_V1"
|
||||
)
|
||||
|
||||
// Values for Notification.EventTypes.
|
||||
const (
|
||||
// Event that occurs when an object is successfully created.
|
||||
ObjectFinalizeEvent = "OBJECT_FINALIZE"
|
||||
|
||||
// Event that occurs when the metadata of an existing object changes.
|
||||
ObjectMetadataUpdateEvent = "OBJECT_METADATA_UPDATE"
|
||||
|
||||
// Event that occurs when an object is permanently deleted.
|
||||
ObjectDeleteEvent = "OBJECT_DELETE"
|
||||
|
||||
// Event that occurs when the live version of an object becomes an
|
||||
// archived version.
|
||||
ObjectArchiveEvent = "OBJECT_ARCHIVE"
|
||||
)
|
||||
|
||||
func toNotification(rn *raw.Notification) *Notification {
|
||||
n := &Notification{
|
||||
ID: rn.Id,
|
||||
EventTypes: rn.EventTypes,
|
||||
ObjectNamePrefix: rn.ObjectNamePrefix,
|
||||
CustomAttributes: rn.CustomAttributes,
|
||||
PayloadFormat: rn.PayloadFormat,
|
||||
}
|
||||
n.TopicProjectID, n.TopicID = parseNotificationTopic(rn.Topic)
|
||||
return n
|
||||
}
|
||||
|
||||
var topicRE = regexp.MustCompile("^//pubsub.googleapis.com/projects/([^/]+)/topics/([^/]+)")
|
||||
|
||||
// parseNotificationTopic extracts the project and topic IDs from from the full
|
||||
// resource name returned by the service. If the name is malformed, it returns
|
||||
// "?" for both IDs.
|
||||
func parseNotificationTopic(nt string) (projectID, topicID string) {
|
||||
matches := topicRE.FindStringSubmatch(nt)
|
||||
if matches == nil {
|
||||
return "?", "?"
|
||||
}
|
||||
return matches[1], matches[2]
|
||||
}
|
||||
|
||||
func toRawNotification(n *Notification) *raw.Notification {
|
||||
return &raw.Notification{
|
||||
Id: n.ID,
|
||||
Topic: fmt.Sprintf("//pubsub.googleapis.com/projects/%s/topics/%s",
|
||||
n.TopicProjectID, n.TopicID),
|
||||
EventTypes: n.EventTypes,
|
||||
ObjectNamePrefix: n.ObjectNamePrefix,
|
||||
CustomAttributes: n.CustomAttributes,
|
||||
PayloadFormat: string(n.PayloadFormat),
|
||||
}
|
||||
}
|
||||
|
||||
// AddNotification adds a notification to b. You must set n's TopicProjectID, TopicID
|
||||
// and PayloadFormat, and must not set its ID. The other fields are all optional. The
|
||||
// returned Notification's ID can be used to refer to it.
|
||||
func (b *BucketHandle) AddNotification(ctx context.Context, n *Notification) (ret *Notification, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.AddNotification")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if n.ID != "" {
|
||||
return nil, errors.New("storage: AddNotification: ID must not be set")
|
||||
}
|
||||
if n.TopicProjectID == "" {
|
||||
return nil, errors.New("storage: AddNotification: missing TopicProjectID")
|
||||
}
|
||||
if n.TopicID == "" {
|
||||
return nil, errors.New("storage: AddNotification: missing TopicID")
|
||||
}
|
||||
call := b.c.raw.Notifications.Insert(b.name, toRawNotification(n))
|
||||
setClientHeader(call.Header())
|
||||
if b.userProject != "" {
|
||||
call.UserProject(b.userProject)
|
||||
}
|
||||
rn, err := call.Context(ctx).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toNotification(rn), nil
|
||||
}
|
||||
|
||||
// Notifications returns all the Notifications configured for this bucket, as a map
|
||||
// indexed by notification ID.
|
||||
func (b *BucketHandle) Notifications(ctx context.Context) (n map[string]*Notification, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Notifications")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
call := b.c.raw.Notifications.List(b.name)
|
||||
setClientHeader(call.Header())
|
||||
if b.userProject != "" {
|
||||
call.UserProject(b.userProject)
|
||||
}
|
||||
var res *raw.Notifications
|
||||
err = runWithRetry(ctx, func() error {
|
||||
res, err = call.Context(ctx).Do()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return notificationsToMap(res.Items), nil
|
||||
}
|
||||
|
||||
func notificationsToMap(rns []*raw.Notification) map[string]*Notification {
|
||||
m := map[string]*Notification{}
|
||||
for _, rn := range rns {
|
||||
m[rn.Id] = toNotification(rn)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// DeleteNotification deletes the notification with the given ID.
|
||||
func (b *BucketHandle) DeleteNotification(ctx context.Context, id string) (err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.DeleteNotification")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
call := b.c.raw.Notifications.Delete(b.name, id)
|
||||
setClientHeader(call.Header())
|
||||
if b.userProject != "" {
|
||||
call.UserProject(b.userProject)
|
||||
}
|
||||
return call.Context(ctx).Do()
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
// Copyright 2016 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/internal/trace"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
var crc32cTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
|
||||
// ReaderObjectAttrs are attributes about the object being read. These are populated
|
||||
// during the New call. This struct only holds a subset of object attributes: to
|
||||
// get the full set of attributes, use ObjectHandle.Attrs.
|
||||
//
|
||||
// Each field is read-only.
|
||||
type ReaderObjectAttrs struct {
|
||||
// Size is the length of the object's content.
|
||||
Size int64
|
||||
|
||||
// ContentType is the MIME type of the object's content.
|
||||
ContentType string
|
||||
|
||||
// ContentEncoding is the encoding of the object's content.
|
||||
ContentEncoding string
|
||||
|
||||
// CacheControl specifies whether and for how long browser and Internet
|
||||
// caches are allowed to cache your objects.
|
||||
CacheControl string
|
||||
|
||||
// LastModified is the time that the object was last modified.
|
||||
LastModified time.Time
|
||||
|
||||
// Generation is the generation number of the object's content.
|
||||
Generation int64
|
||||
|
||||
// Metageneration is the version of the metadata for this object at
|
||||
// this generation. This field is used for preconditions and for
|
||||
// detecting changes in metadata. A metageneration number is only
|
||||
// meaningful in the context of a particular generation of a
|
||||
// particular object.
|
||||
Metageneration int64
|
||||
}
|
||||
|
||||
// NewReader creates a new Reader to read the contents of the
|
||||
// object.
|
||||
// ErrObjectNotExist will be returned if the object is not found.
|
||||
//
|
||||
// The caller must call Close on the returned Reader when done reading.
|
||||
func (o *ObjectHandle) NewReader(ctx context.Context) (*Reader, error) {
|
||||
return o.NewRangeReader(ctx, 0, -1)
|
||||
}
|
||||
|
||||
// NewRangeReader reads part of an object, reading at most length bytes
|
||||
// starting at the given offset. If length is negative, the object is read
|
||||
// until the end.
|
||||
func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64) (r *Reader, err error) {
|
||||
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.NewRangeReader")
|
||||
defer func() { trace.EndSpan(ctx, err) }()
|
||||
|
||||
if err := o.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if offset < 0 {
|
||||
return nil, fmt.Errorf("storage: invalid offset %d < 0", offset)
|
||||
}
|
||||
if o.conds != nil {
|
||||
if err := o.conds.validate("NewRangeReader"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "storage.googleapis.com",
|
||||
Path: fmt.Sprintf("/%s/%s", o.bucket, o.object),
|
||||
}
|
||||
verb := "GET"
|
||||
if length == 0 {
|
||||
verb = "HEAD"
|
||||
}
|
||||
req, err := http.NewRequest(verb, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if o.userProject != "" {
|
||||
req.Header.Set("X-Goog-User-Project", o.userProject)
|
||||
}
|
||||
if o.readCompressed {
|
||||
req.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
if err := setEncryptionHeaders(req.Header, o.encryptionKey, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gen := o.gen
|
||||
|
||||
// Define a function that initiates a Read with offset and length, assuming we
|
||||
// have already read seen bytes.
|
||||
reopen := func(seen int64) (*http.Response, error) {
|
||||
start := offset + seen
|
||||
if length < 0 && start > 0 {
|
||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", start))
|
||||
} else if length > 0 {
|
||||
// The end character isn't affected by how many bytes we've seen.
|
||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, offset+length-1))
|
||||
}
|
||||
// We wait to assign conditions here because the generation number can change in between reopen() runs.
|
||||
req.URL.RawQuery = conditionsQuery(gen, o.conds)
|
||||
var res *http.Response
|
||||
err = runWithRetry(ctx, func() error {
|
||||
res, err = o.c.hc.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode == http.StatusNotFound {
|
||||
res.Body.Close()
|
||||
return ErrObjectNotExist
|
||||
}
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
return &googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
Body: string(body),
|
||||
}
|
||||
}
|
||||
if start > 0 && length != 0 && res.StatusCode != http.StatusPartialContent {
|
||||
res.Body.Close()
|
||||
return errors.New("storage: partial request not satisfied")
|
||||
}
|
||||
// If a generation hasn't been specified, and this is the first response we get, let's record the
|
||||
// generation. In future requests we'll use this generation as a precondition to avoid data races.
|
||||
if gen < 0 && res.Header.Get("X-Goog-Generation") != "" {
|
||||
gen64, err := strconv.ParseInt(res.Header.Get("X-Goog-Generation"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gen = gen64
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res, err := reopen(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
size int64 // total size of object, even if a range was requested.
|
||||
checkCRC bool
|
||||
crc uint32
|
||||
)
|
||||
if res.StatusCode == http.StatusPartialContent {
|
||||
cr := strings.TrimSpace(res.Header.Get("Content-Range"))
|
||||
if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") {
|
||||
|
||||
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
|
||||
}
|
||||
size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
|
||||
}
|
||||
} else {
|
||||
size = res.ContentLength
|
||||
// Check the CRC iff all of the following hold:
|
||||
// - We asked for content (length != 0).
|
||||
// - We got all the content (status != PartialContent).
|
||||
// - The server sent a CRC header.
|
||||
// - The Go http stack did not uncompress the file.
|
||||
// - We were not served compressed data that was uncompressed on download.
|
||||
// The problem with the last two cases is that the CRC will not match -- GCS
|
||||
// computes it on the compressed contents, but we compute it on the
|
||||
// uncompressed contents.
|
||||
if length != 0 && !res.Uncompressed && !uncompressedByServer(res) {
|
||||
crc, checkCRC = parseCRC32c(res)
|
||||
}
|
||||
}
|
||||
|
||||
remain := res.ContentLength
|
||||
body := res.Body
|
||||
if length == 0 {
|
||||
remain = 0
|
||||
body.Close()
|
||||
body = emptyBody
|
||||
}
|
||||
var metaGen int64
|
||||
if res.Header.Get("X-Goog-Generation") != "" {
|
||||
metaGen, err = strconv.ParseInt(res.Header.Get("X-Goog-Metageneration"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var lm time.Time
|
||||
if res.Header.Get("Last-Modified") != "" {
|
||||
lm, err = http.ParseTime(res.Header.Get("Last-Modified"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
attrs := ReaderObjectAttrs{
|
||||
Size: size,
|
||||
ContentType: res.Header.Get("Content-Type"),
|
||||
ContentEncoding: res.Header.Get("Content-Encoding"),
|
||||
CacheControl: res.Header.Get("Cache-Control"),
|
||||
LastModified: lm,
|
||||
Generation: gen,
|
||||
Metageneration: metaGen,
|
||||
}
|
||||
return &Reader{
|
||||
Attrs: attrs,
|
||||
body: body,
|
||||
size: size,
|
||||
remain: remain,
|
||||
wantCRC: crc,
|
||||
checkCRC: checkCRC,
|
||||
reopen: reopen,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func uncompressedByServer(res *http.Response) bool {
|
||||
// If the data is stored as gzip but is not encoded as gzip, then it
|
||||
// was uncompressed by the server.
|
||||
return res.Header.Get("X-Goog-Stored-Content-Encoding") == "gzip" &&
|
||||
res.Header.Get("Content-Encoding") != "gzip"
|
||||
}
|
||||
|
||||
func parseCRC32c(res *http.Response) (uint32, bool) {
|
||||
const prefix = "crc32c="
|
||||
for _, spec := range res.Header["X-Goog-Hash"] {
|
||||
if strings.HasPrefix(spec, prefix) {
|
||||
c, err := decodeUint32(spec[len(prefix):])
|
||||
if err == nil {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
var emptyBody = ioutil.NopCloser(strings.NewReader(""))
|
||||
|
||||
// Reader reads a Cloud Storage object.
|
||||
// It implements io.Reader.
|
||||
//
|
||||
// Typically, a Reader computes the CRC of the downloaded content and compares it to
|
||||
// the stored CRC, returning an error from Read if there is a mismatch. This integrity check
|
||||
// is skipped if transcoding occurs. See https://cloud.google.com/storage/docs/transcoding.
|
||||
type Reader struct {
|
||||
Attrs ReaderObjectAttrs
|
||||
body io.ReadCloser
|
||||
seen, remain, size int64
|
||||
checkCRC bool // should we check the CRC?
|
||||
wantCRC uint32 // the CRC32c value the server sent in the header
|
||||
gotCRC uint32 // running crc
|
||||
reopen func(seen int64) (*http.Response, error)
|
||||
}
|
||||
|
||||
// Close closes the Reader. It must be called when done reading.
|
||||
func (r *Reader) Close() error {
|
||||
return r.body.Close()
|
||||
}
|
||||
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
n, err := r.readWithRetry(p)
|
||||
if r.remain != -1 {
|
||||
r.remain -= int64(n)
|
||||
}
|
||||
if r.checkCRC {
|
||||
r.gotCRC = crc32.Update(r.gotCRC, crc32cTable, p[:n])
|
||||
// Check CRC here. It would be natural to check it in Close, but
|
||||
// everybody defers Close on the assumption that it doesn't return
|
||||
// anything worth looking at.
|
||||
if err == io.EOF {
|
||||
if r.gotCRC != r.wantCRC {
|
||||
return n, fmt.Errorf("storage: bad CRC on read: got %d, want %d",
|
||||
r.gotCRC, r.wantCRC)
|
||||
}
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *Reader) readWithRetry(p []byte) (int, error) {
|
||||
n := 0
|
||||
for len(p[n:]) > 0 {
|
||||
m, err := r.body.Read(p[n:])
|
||||
n += m
|
||||
r.seen += int64(m)
|
||||
if !shouldRetryRead(err) {
|
||||
return n, err
|
||||
}
|
||||
// Read failed, but we will try again. Send a ranged read request that takes
|
||||
// into account the number of bytes we've already seen.
|
||||
res, err := r.reopen(r.seen)
|
||||
if err != nil {
|
||||
// reopen already retries
|
||||
return n, err
|
||||
}
|
||||
r.body.Close()
|
||||
r.body = res.Body
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func shouldRetryRead(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return strings.HasSuffix(err.Error(), "INTERNAL_ERROR") && strings.Contains(reflect.TypeOf(err).String(), "http2")
|
||||
}
|
||||
|
||||
// Size returns the size of the object in bytes.
|
||||
// The returned value is always the same and is not affected by
|
||||
// calls to Read or Close.
|
||||
//
|
||||
// Deprecated: use Reader.Attrs.Size.
|
||||
func (r *Reader) Size() int64 {
|
||||
return r.Attrs.Size
|
||||
}
|
||||
|
||||
// Remain returns the number of bytes left to read, or -1 if unknown.
|
||||
func (r *Reader) Remain() int64 {
|
||||
return r.remain
|
||||
}
|
||||
|
||||
// ContentType returns the content type of the object.
|
||||
//
|
||||
// Deprecated: use Reader.Attrs.ContentType.
|
||||
func (r *Reader) ContentType() string {
|
||||
return r.Attrs.ContentType
|
||||
}
|
||||
|
||||
// ContentEncoding returns the content encoding of the object.
|
||||
//
|
||||
// Deprecated: use Reader.Attrs.ContentEncoding.
|
||||
func (r *Reader) ContentEncoding() string {
|
||||
return r.Attrs.ContentEncoding
|
||||
}
|
||||
|
||||
// CacheControl returns the cache control of the object.
|
||||
//
|
||||
// Deprecated: use Reader.Attrs.CacheControl.
|
||||
func (r *Reader) CacheControl() string {
|
||||
return r.Attrs.CacheControl
|
||||
}
|
||||
|
||||
// LastModified returns the value of the Last-Modified header.
|
||||
//
|
||||
// Deprecated: use Reader.Attrs.LastModified.
|
||||
func (r *Reader) LastModified() (time.Time, error) {
|
||||
return r.Attrs.LastModified, nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,261 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License 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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
raw "google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
// A Writer writes a Cloud Storage object.
|
||||
type Writer struct {
|
||||
// ObjectAttrs are optional attributes to set on the object. Any attributes
|
||||
// must be initialized before the first Write call. Nil or zero-valued
|
||||
// attributes are ignored.
|
||||
ObjectAttrs
|
||||
|
||||
// SendCRC specifies whether to transmit a CRC32C field. It should be set
|
||||
// to true in addition to setting the Writer's CRC32C field, because zero
|
||||
// is a valid CRC and normally a zero would not be transmitted.
|
||||
// If a CRC32C is sent, and the data written does not match the checksum,
|
||||
// the write will be rejected.
|
||||
SendCRC32C bool
|
||||
|
||||
// ChunkSize controls the maximum number of bytes of the object that the
|
||||
// Writer will attempt to send to the server in a single request. Objects
|
||||
// smaller than the size will be sent in a single request, while larger
|
||||
// objects will be split over multiple requests. The size will be rounded up
|
||||
// to the nearest multiple of 256K. If zero, chunking will be disabled and
|
||||
// the object will be uploaded in a single request.
|
||||
//
|
||||
// ChunkSize will default to a reasonable value. If you perform many concurrent
|
||||
// writes of small objects, you may wish set ChunkSize to a value that matches
|
||||
// your objects' sizes to avoid consuming large amounts of memory.
|
||||
//
|
||||
// ChunkSize must be set before the first Write call.
|
||||
ChunkSize int
|
||||
|
||||
// ProgressFunc can be used to monitor the progress of a large write.
|
||||
// operation. If ProgressFunc is not nil and writing requires multiple
|
||||
// calls to the underlying service (see
|
||||
// https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload),
|
||||
// then ProgressFunc will be invoked after each call with the number of bytes of
|
||||
// content copied so far.
|
||||
//
|
||||
// ProgressFunc should return quickly without blocking.
|
||||
ProgressFunc func(int64)
|
||||
|
||||
ctx context.Context
|
||||
o *ObjectHandle
|
||||
|
||||
opened bool
|
||||
pw *io.PipeWriter
|
||||
|
||||
donec chan struct{} // closed after err and obj are set.
|
||||
obj *ObjectAttrs
|
||||
|
||||
mu sync.Mutex
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *Writer) open() error {
|
||||
attrs := w.ObjectAttrs
|
||||
// Check the developer didn't change the object Name (this is unfortunate, but
|
||||
// we don't want to store an object under the wrong name).
|
||||
if attrs.Name != w.o.object {
|
||||
return fmt.Errorf("storage: Writer.Name %q does not match object name %q", attrs.Name, w.o.object)
|
||||
}
|
||||
if !utf8.ValidString(attrs.Name) {
|
||||
return fmt.Errorf("storage: object name %q is not valid UTF-8", attrs.Name)
|
||||
}
|
||||
if attrs.KMSKeyName != "" && w.o.encryptionKey != nil {
|
||||
return errors.New("storage: cannot use KMSKeyName with a customer-supplied encryption key")
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
w.pw = pw
|
||||
w.opened = true
|
||||
|
||||
go w.monitorCancel()
|
||||
|
||||
if w.ChunkSize < 0 {
|
||||
return errors.New("storage: Writer.ChunkSize must be non-negative")
|
||||
}
|
||||
mediaOpts := []googleapi.MediaOption{
|
||||
googleapi.ChunkSize(w.ChunkSize),
|
||||
}
|
||||
if c := attrs.ContentType; c != "" {
|
||||
mediaOpts = append(mediaOpts, googleapi.ContentType(c))
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(w.donec)
|
||||
|
||||
rawObj := attrs.toRawObject(w.o.bucket)
|
||||
if w.SendCRC32C {
|
||||
rawObj.Crc32c = encodeUint32(attrs.CRC32C)
|
||||
}
|
||||
if w.MD5 != nil {
|
||||
rawObj.Md5Hash = base64.StdEncoding.EncodeToString(w.MD5)
|
||||
}
|
||||
call := w.o.c.raw.Objects.Insert(w.o.bucket, rawObj).
|
||||
Media(pr, mediaOpts...).
|
||||
Projection("full").
|
||||
Context(w.ctx)
|
||||
if w.ProgressFunc != nil {
|
||||
call.ProgressUpdater(func(n, _ int64) { w.ProgressFunc(n) })
|
||||
}
|
||||
if attrs.KMSKeyName != "" {
|
||||
call.KmsKeyName(attrs.KMSKeyName)
|
||||
}
|
||||
if attrs.PredefinedACL != "" {
|
||||
call.PredefinedAcl(attrs.PredefinedACL)
|
||||
}
|
||||
if err := setEncryptionHeaders(call.Header(), w.o.encryptionKey, false); err != nil {
|
||||
w.mu.Lock()
|
||||
w.err = err
|
||||
w.mu.Unlock()
|
||||
pr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
var resp *raw.Object
|
||||
err := applyConds("NewWriter", w.o.gen, w.o.conds, call)
|
||||
if err == nil {
|
||||
if w.o.userProject != "" {
|
||||
call.UserProject(w.o.userProject)
|
||||
}
|
||||
setClientHeader(call.Header())
|
||||
// If the chunk size is zero, then no chunking is done on the Reader,
|
||||
// which means we cannot retry: the first call will read the data, and if
|
||||
// it fails, there is no way to re-read.
|
||||
if w.ChunkSize == 0 {
|
||||
resp, err = call.Do()
|
||||
} else {
|
||||
// We will only retry here if the initial POST, which obtains a URI for
|
||||
// the resumable upload, fails with a retryable error. The upload itself
|
||||
// has its own retry logic.
|
||||
err = runWithRetry(w.ctx, func() error {
|
||||
var err2 error
|
||||
resp, err2 = call.Do()
|
||||
return err2
|
||||
})
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
w.mu.Lock()
|
||||
w.err = err
|
||||
w.mu.Unlock()
|
||||
pr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
w.obj = newObject(resp)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write appends to w. It implements the io.Writer interface.
|
||||
//
|
||||
// Since writes happen asynchronously, Write may return a nil
|
||||
// error even though the write failed (or will fail). Always
|
||||
// use the error returned from Writer.Close to determine if
|
||||
// the upload was successful.
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
w.mu.Lock()
|
||||
werr := w.err
|
||||
w.mu.Unlock()
|
||||
if werr != nil {
|
||||
return 0, werr
|
||||
}
|
||||
if !w.opened {
|
||||
if err := w.open(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
n, err = w.pw.Write(p)
|
||||
if err != nil {
|
||||
w.mu.Lock()
|
||||
werr := w.err
|
||||
w.mu.Unlock()
|
||||
// Preserve existing functionality that when context is canceled, Write will return
|
||||
// context.Canceled instead of "io: read/write on closed pipe". This hides the
|
||||
// pipe implementation detail from users and makes Write seem as though it's an RPC.
|
||||
if werr == context.Canceled || werr == context.DeadlineExceeded {
|
||||
return n, werr
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close completes the write operation and flushes any buffered data.
|
||||
// If Close doesn't return an error, metadata about the written object
|
||||
// can be retrieved by calling Attrs.
|
||||
func (w *Writer) Close() error {
|
||||
if !w.opened {
|
||||
if err := w.open(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Closing either the read or write causes the entire pipe to close.
|
||||
if err := w.pw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-w.donec
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.err
|
||||
}
|
||||
|
||||
// monitorCancel is intended to be used as a background goroutine. It monitors the
|
||||
// the context, and when it observes that the context has been canceled, it manually
|
||||
// closes things that do not take a context.
|
||||
func (w *Writer) monitorCancel() {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
w.mu.Lock()
|
||||
werr := w.ctx.Err()
|
||||
w.err = werr
|
||||
w.mu.Unlock()
|
||||
|
||||
// Closing either the read or write causes the entire pipe to close.
|
||||
w.CloseWithError(werr)
|
||||
case <-w.donec:
|
||||
}
|
||||
}
|
||||
|
||||
// CloseWithError aborts the write operation with the provided error.
|
||||
// CloseWithError always returns nil.
|
||||
//
|
||||
// Deprecated: cancel the context passed to NewWriter instead.
|
||||
func (w *Writer) CloseWithError(err error) error {
|
||||
if !w.opened {
|
||||
return nil
|
||||
}
|
||||
return w.pw.CloseWithError(err)
|
||||
}
|
||||
|
||||
// Attrs returns metadata about a successfully-written object.
|
||||
// It's only valid to call it after Close returns nil.
|
||||
func (w *Writer) Attrs() *ObjectAttrs {
|
||||
return w.obj
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
AWS SDK for Go
|
||||
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Copyright 2014-2015 Stripe, Inc.
|
||||
|
|
|
@ -15,7 +15,7 @@ func DeepEqual(a, b interface{}) bool {
|
|||
rb := reflect.Indirect(reflect.ValueOf(b))
|
||||
|
||||
if raValid, rbValid := ra.IsValid(), rb.IsValid(); !raValid && !rbValid {
|
||||
// If the elements are both nil, and of the same type the are equal
|
||||
// If the elements are both nil, and of the same type they are equal
|
||||
// If they are of different types they are not equal
|
||||
return reflect.TypeOf(a) == reflect.TypeOf(b)
|
||||
} else if raValid != rbValid {
|
||||
|
|
|
@ -118,6 +118,12 @@ var LogHTTPResponseHandler = request.NamedHandler{
|
|||
func logResponse(r *request.Request) {
|
||||
lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)}
|
||||
|
||||
if r.HTTPResponse == nil {
|
||||
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, "request's HTTPResponse is nil"))
|
||||
return
|
||||
}
|
||||
|
||||
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||
if logBody {
|
||||
r.HTTPResponse.Body = &teeReaderCloser{
|
||||
|
|
21
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
21
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
|
@ -80,16 +80,18 @@ package stscreds
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
)
|
||||
|
||||
// StdinTokenProvider will prompt on stdout and read from stdin for a string value.
|
||||
// StdinTokenProvider will prompt on stderr and read from stdin for a string value.
|
||||
// An error is returned if reading from stdin fails.
|
||||
//
|
||||
// Use this function go read MFA tokens from stdin. The function makes no attempt
|
||||
|
@ -102,7 +104,7 @@ import (
|
|||
// Will wait forever until something is provided on the stdin.
|
||||
func StdinTokenProvider() (string, error) {
|
||||
var v string
|
||||
fmt.Printf("Assume Role MFA token code: ")
|
||||
fmt.Fprintf(os.Stderr, "Assume Role MFA token code: ")
|
||||
_, err := fmt.Scanln(&v)
|
||||
|
||||
return v, err
|
||||
|
@ -193,6 +195,18 @@ type AssumeRoleProvider struct {
|
|||
//
|
||||
// If ExpiryWindow is 0 or less it will be ignored.
|
||||
ExpiryWindow time.Duration
|
||||
|
||||
// MaxJitterFrac reduces the effective Duration of each credential requested
|
||||
// by a random percentage between 0 and MaxJitterFraction. MaxJitterFrac must
|
||||
// have a value between 0 and 1. Any other value may lead to expected behavior.
|
||||
// With a MaxJitterFrac value of 0, default) will no jitter will be used.
|
||||
//
|
||||
// For example, with a Duration of 30m and a MaxJitterFrac of 0.1, the
|
||||
// AssumeRole call will be made with an arbitrary Duration between 27m and
|
||||
// 30m.
|
||||
//
|
||||
// MaxJitterFrac should not be negative.
|
||||
MaxJitterFrac float64
|
||||
}
|
||||
|
||||
// NewCredentials returns a pointer to a new Credentials object wrapping the
|
||||
|
@ -254,8 +268,9 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
|
|||
// Expire as often as AWS permits.
|
||||
p.Duration = DefaultDuration
|
||||
}
|
||||
jitter := time.Duration(sdkrand.SeededRand.Float64() * p.MaxJitterFrac * float64(p.Duration))
|
||||
input := &sts.AssumeRoleInput{
|
||||
DurationSeconds: aws.Int64(int64(p.Duration / time.Second)),
|
||||
DurationSeconds: aws.Int64(int64((p.Duration - jitter) / time.Second)),
|
||||
RoleArn: aws.String(p.RoleARN),
|
||||
RoleSessionName: aws.String(p.RoleSessionName),
|
||||
ExternalId: p.ExternalID,
|
||||
|
|
|
@ -24,8 +24,9 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
|||
|
||||
output := &metadataOutput{}
|
||||
req := c.NewRequest(op, nil, output)
|
||||
err := req.Send()
|
||||
|
||||
return output.Content, req.Send()
|
||||
return output.Content, err
|
||||
}
|
||||
|
||||
// GetUserData returns the userdata that was configured for the service. If
|
||||
|
@ -45,8 +46,9 @@ func (c *EC2Metadata) GetUserData() (string, error) {
|
|||
r.Error = awserr.New("NotFoundError", "user-data not found", r.Error)
|
||||
}
|
||||
})
|
||||
err := req.Send()
|
||||
|
||||
return output.Content, req.Send()
|
||||
return output.Content, err
|
||||
}
|
||||
|
||||
// GetDynamicData uses the path provided to request information from the EC2
|
||||
|
@ -61,8 +63,9 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
|||
|
||||
output := &metadataOutput{}
|
||||
req := c.NewRequest(op, nil, output)
|
||||
err := req.Send()
|
||||
|
||||
return output.Content, req.Send()
|
||||
return output.Content, err
|
||||
}
|
||||
|
||||
// GetInstanceIdentityDocument retrieves an identity document describing an
|
||||
|
|
|
@ -92,6 +92,9 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
svc.Handlers.Send.SwapNamed(request.NamedHandler{
|
||||
Name: corehandlers.SendHandler.Name,
|
||||
Fn: func(r *request.Request) {
|
||||
r.HTTPResponse = &http.Response{
|
||||
Header: http.Header{},
|
||||
}
|
||||
r.Error = awserr.New(
|
||||
request.CanceledErrorCode,
|
||||
"EC2 IMDS access disabled via "+disableServiceEnvVar+" env var",
|
||||
|
|
|
@ -15,6 +15,7 @@ const (
|
|||
|
||||
// AWS Standard partition's regions.
|
||||
const (
|
||||
ApEast1RegionID = "ap-east-1" // Asia Pacific (Hong Kong).
|
||||
ApNortheast1RegionID = "ap-northeast-1" // Asia Pacific (Tokyo).
|
||||
ApNortheast2RegionID = "ap-northeast-2" // Asia Pacific (Seoul).
|
||||
ApSouth1RegionID = "ap-south-1" // Asia Pacific (Mumbai).
|
||||
|
@ -91,6 +92,9 @@ var awsPartition = partition{
|
|||
SignatureVersions: []string{"v4"},
|
||||
},
|
||||
Regions: regions{
|
||||
"ap-east-1": region{
|
||||
Description: "Asia Pacific (Hong Kong)",
|
||||
},
|
||||
"ap-northeast-1": region{
|
||||
Description: "Asia Pacific (Tokyo)",
|
||||
},
|
||||
|
@ -150,6 +154,7 @@ var awsPartition = partition{
|
|||
"acm": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -174,20 +179,31 @@ var awsPartition = partition{
|
|||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"api.ecr": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{
|
||||
Hostname: "api.ecr.ap-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-east-1",
|
||||
},
|
||||
},
|
||||
"ap-northeast-1": endpoint{
|
||||
Hostname: "api.ecr.ap-northeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -353,6 +369,7 @@ var awsPartition = partition{
|
|||
"apigateway": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -380,6 +397,7 @@ var awsPartition = partition{
|
|||
},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -426,6 +444,7 @@ var awsPartition = partition{
|
|||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
|
@ -439,6 +458,7 @@ var awsPartition = partition{
|
|||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
|
@ -452,6 +472,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -561,6 +582,7 @@ var awsPartition = partition{
|
|||
"cloud9": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
|
@ -585,6 +607,7 @@ var awsPartition = partition{
|
|||
"cloudformation": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -674,6 +697,7 @@ var awsPartition = partition{
|
|||
"cloudtrail": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -904,14 +928,25 @@ var awsPartition = partition{
|
|||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"comprehendmedical": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"config": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -968,6 +1003,7 @@ var awsPartition = partition{
|
|||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
|
@ -1012,6 +1048,7 @@ var awsPartition = partition{
|
|||
"dms": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1033,6 +1070,12 @@ var awsPartition = partition{
|
|||
"docdb": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-central-1": endpoint{
|
||||
Hostname: "rds.eu-central-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-central-1",
|
||||
},
|
||||
},
|
||||
"eu-west-1": endpoint{
|
||||
Hostname: "rds.eu-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -1083,6 +1126,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1113,6 +1157,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1145,6 +1190,7 @@ var awsPartition = partition{
|
|||
"ecs": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1166,6 +1212,7 @@ var awsPartition = partition{
|
|||
"elasticache": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1232,6 +1279,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1256,6 +1304,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1294,9 +1343,10 @@ var awsPartition = partition{
|
|||
"email": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"entitlement.marketplace": service{
|
||||
|
@ -1312,6 +1362,7 @@ var awsPartition = partition{
|
|||
"es": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1339,6 +1390,7 @@ var awsPartition = partition{
|
|||
"events": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1394,10 +1446,12 @@ var awsPartition = partition{
|
|||
"fsx": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"gamelift": service{
|
||||
|
@ -1424,6 +1478,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1581,6 +1636,7 @@ var awsPartition = partition{
|
|||
"kinesis": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1613,6 +1669,7 @@ var awsPartition = partition{
|
|||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
|
@ -1628,6 +1685,7 @@ var awsPartition = partition{
|
|||
Region: "ca-central-1",
|
||||
},
|
||||
},
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1649,6 +1707,7 @@ var awsPartition = partition{
|
|||
"lambda": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1704,6 +1763,7 @@ var awsPartition = partition{
|
|||
"logs": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1735,6 +1795,25 @@ var awsPartition = partition{
|
|||
"us-east-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"mediaconnect": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"mediaconvert": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -1747,6 +1826,7 @@ var awsPartition = partition{
|
|||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
|
@ -1805,6 +1885,7 @@ var awsPartition = partition{
|
|||
},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1852,6 +1933,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -1879,6 +1961,7 @@ var awsPartition = partition{
|
|||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
|
@ -1904,6 +1987,12 @@ var awsPartition = partition{
|
|||
Region: "ap-northeast-1",
|
||||
},
|
||||
},
|
||||
"ap-south-1": endpoint{
|
||||
Hostname: "rds.ap-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-south-1",
|
||||
},
|
||||
},
|
||||
"ap-southeast-1": endpoint{
|
||||
Hostname: "rds.ap-southeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -2008,10 +2097,12 @@ var awsPartition = partition{
|
|||
},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"polly": service{
|
||||
|
@ -2024,6 +2115,7 @@ var awsPartition = partition{
|
|||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
|
@ -2037,6 +2129,7 @@ var awsPartition = partition{
|
|||
"rds": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2060,6 +2153,7 @@ var awsPartition = partition{
|
|||
"redshift": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2094,6 +2188,7 @@ var awsPartition = partition{
|
|||
"resource-groups": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2115,9 +2210,10 @@ var awsPartition = partition{
|
|||
"robomaker": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"route53": service{
|
||||
|
@ -2145,11 +2241,18 @@ var awsPartition = partition{
|
|||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
|
@ -2194,6 +2297,7 @@ var awsPartition = partition{
|
|||
DualStackHostname: "{service}.dualstack.{region}.{dnsSuffix}",
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{
|
||||
Hostname: "s3.ap-northeast-1.amazonaws.com",
|
||||
SignatureVersions: []string{"s3", "s3v4"},
|
||||
|
@ -2422,6 +2526,7 @@ var awsPartition = partition{
|
|||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
|
@ -2502,12 +2607,18 @@ var awsPartition = partition{
|
|||
"eu-central-1": endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
"eu-north-1": endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
"eu-west-1": endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
"eu-west-2": endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
"eu-west-3": endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
"sa-east-1": endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
|
@ -2610,6 +2721,7 @@ var awsPartition = partition{
|
|||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
|
@ -2644,6 +2756,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2668,6 +2781,7 @@ var awsPartition = partition{
|
|||
Protocols: []string{"http", "https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2715,6 +2829,7 @@ var awsPartition = partition{
|
|||
"ssm": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2736,6 +2851,7 @@ var awsPartition = partition{
|
|||
"states": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2817,6 +2933,12 @@ var awsPartition = partition{
|
|||
},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{
|
||||
Hostname: "sts.ap-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-east-1",
|
||||
},
|
||||
},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{
|
||||
Hostname: "sts.ap-northeast-2.amazonaws.com",
|
||||
|
@ -2874,6 +2996,7 @@ var awsPartition = partition{
|
|||
"swf": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2895,6 +3018,7 @@ var awsPartition = partition{
|
|||
"tagging": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
|
@ -2937,8 +3061,10 @@ var awsPartition = partition{
|
|||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-1-fips": endpoint{
|
||||
Hostname: "translate-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -2978,9 +3104,13 @@ var awsPartition = partition{
|
|||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
|
@ -3130,6 +3260,20 @@ var awscnPartition = partition{
|
|||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"cloudfront": service{
|
||||
PartitionEndpoint: "aws-cn-global",
|
||||
IsRegionalized: boxedFalse,
|
||||
|
||||
Endpoints: endpoints{
|
||||
"aws-cn-global": endpoint{
|
||||
Hostname: "cloudfront.cn-northwest-1.amazonaws.com.cn",
|
||||
Protocols: []string{"http", "https"},
|
||||
CredentialScope: credentialScope{
|
||||
Region: "cn-northwest-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"cloudtrail": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -3333,6 +3477,12 @@ var awscnPartition = partition{
|
|||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"mediaconvert": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"monitoring": service{
|
||||
Defaults: endpoint{
|
||||
Protocols: []string{"http", "https"},
|
||||
|
@ -3612,6 +3762,12 @@ var awsusgovPartition = partition{
|
|||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"codecommit": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"codedeploy": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -3631,6 +3787,14 @@ var awsusgovPartition = partition{
|
|||
},
|
||||
},
|
||||
},
|
||||
"comprehend": service{
|
||||
Defaults: endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"config": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -3847,6 +4011,13 @@ var awsusgovPartition = partition{
|
|||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"license-manager": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"logs": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -4071,6 +4242,12 @@ var awsusgovPartition = partition{
|
|||
},
|
||||
},
|
||||
},
|
||||
"waf-regional": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"workspaces": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
|
|
@ -38,6 +38,7 @@ var throttleCodes = map[string]struct{}{
|
|||
"ThrottlingException": {},
|
||||
"RequestLimitExceeded": {},
|
||||
"RequestThrottled": {},
|
||||
"RequestThrottledException": {},
|
||||
"TooManyRequestsException": {}, // Lambda functions
|
||||
"PriorRequestNotComplete": {}, // Route53
|
||||
"TransactionInProgressException": {},
|
||||
|
|
26
vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport.go
generated
vendored
Normal file
26
vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
// +build go1.7
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Transport that should be used when a custom CA bundle is specified with the
|
||||
// SDK.
|
||||
func getCABundleTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
22
vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_5.go
generated
vendored
Normal file
22
vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_5.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
// +build !go1.6,go1.5
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Transport that should be used when a custom CA bundle is specified with the
|
||||
// SDK.
|
||||
func getCABundleTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
}
|
23
vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_6.go
generated
vendored
Normal file
23
vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_6.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
// +build !go1.7,go1.6
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Transport that should be used when a custom CA bundle is specified with the
|
||||
// SDK.
|
||||
func getCABundleTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
|
@ -407,7 +407,10 @@ func loadCustomCABundle(s *Session, bundle io.Reader) error {
|
|||
}
|
||||
}
|
||||
if t == nil {
|
||||
t = &http.Transport{}
|
||||
// Nil transport implies `http.DefaultTransport` should be used. Since
|
||||
// the SDK cannot modify, nor copy the `DefaultTransport` specifying
|
||||
// the values the next closest behavior.
|
||||
t = getCABundleTransport()
|
||||
}
|
||||
|
||||
p, err := loadCertPool(bundle)
|
||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
|||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.16.36"
|
||||
const SDKVersion = "1.19.18"
|
||||
|
|
|
@ -155,6 +155,9 @@ func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect.
|
|||
return awserr.New("SerializationError", "failed to encode REST request", err)
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
str = strings.TrimSpace(str)
|
||||
|
||||
header.Add(name, str)
|
||||
|
||||
return nil
|
||||
|
@ -170,8 +173,10 @@ func buildHeaderMap(header *http.Header, v reflect.Value, tag reflect.StructTag)
|
|||
return awserr.New("SerializationError", "failed to encode REST request", err)
|
||||
|
||||
}
|
||||
keyStr := strings.TrimSpace(key.String())
|
||||
str = strings.TrimSpace(str)
|
||||
|
||||
header.Add(prefix+key.String(), str)
|
||||
header.Add(prefix+keyStr, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2047,7 +2047,7 @@ func (c *S3) GetBucketLifecycleRequest(input *GetBucketLifecycleInput) (req *req
|
|||
|
||||
// GetBucketLifecycle API operation for Amazon Simple Storage Service.
|
||||
//
|
||||
// Deprecated, see the GetBucketLifecycleConfiguration operation.
|
||||
// No longer used, see the GetBucketLifecycleConfiguration operation.
|
||||
//
|
||||
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
|
||||
// with awserr.Error's Code and Message methods to get detailed information about
|
||||
|
@ -2428,7 +2428,7 @@ func (c *S3) GetBucketNotificationRequest(input *GetBucketNotificationConfigurat
|
|||
|
||||
// GetBucketNotification API operation for Amazon Simple Storage Service.
|
||||
//
|
||||
// Deprecated, see the GetBucketNotificationConfiguration operation.
|
||||
// No longer used, see the GetBucketNotificationConfiguration operation.
|
||||
//
|
||||
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
|
||||
// with awserr.Error's Code and Message methods to get detailed information about
|
||||
|
@ -5287,7 +5287,7 @@ func (c *S3) PutBucketLifecycleRequest(input *PutBucketLifecycleInput) (req *req
|
|||
|
||||
// PutBucketLifecycle API operation for Amazon Simple Storage Service.
|
||||
//
|
||||
// Deprecated, see the PutBucketLifecycleConfiguration operation.
|
||||
// No longer used, see the PutBucketLifecycleConfiguration operation.
|
||||
//
|
||||
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
|
||||
// with awserr.Error's Code and Message methods to get detailed information about
|
||||
|
@ -5600,7 +5600,7 @@ func (c *S3) PutBucketNotificationRequest(input *PutBucketNotificationInput) (re
|
|||
|
||||
// PutBucketNotification API operation for Amazon Simple Storage Service.
|
||||
//
|
||||
// Deprecated, see the PutBucketNotificationConfiguraiton operation.
|
||||
// No longer used, see the PutBucketNotificationConfiguration operation.
|
||||
//
|
||||
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
|
||||
// with awserr.Error's Code and Message methods to get detailed information about
|
||||
|
@ -8937,7 +8937,7 @@ type CreateBucketConfiguration struct {
|
|||
_ struct{} `type:"structure"`
|
||||
|
||||
// Specifies the region where the bucket will be created. If you don't specify
|
||||
// a region, the bucket will be created in US Standard.
|
||||
// a region, the bucket is created in US East (N. Virginia) Region (us-east-1).
|
||||
LocationConstraint *string `type:"string" enum:"BucketLocationConstraint"`
|
||||
}
|
||||
|
||||
|
@ -11215,7 +11215,7 @@ type FilterRule struct {
|
|||
// The object key name prefix or suffix identifying one or more objects to which
|
||||
// the filtering rule applies. The maximum prefix length is 1,024 characters.
|
||||
// Overlapping prefixes and suffixes are not supported. For more information,
|
||||
// see Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// see Configuring Event Notifications (https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Name *string `type:"string" enum:"FilterRuleName"`
|
||||
|
||||
|
@ -15149,7 +15149,7 @@ type LambdaFunctionConfiguration struct {
|
|||
Events []*string `locationName:"Event" type:"list" flattened:"true" required:"true"`
|
||||
|
||||
// A container for object key name filtering rules. For information about key
|
||||
// name filtering, see Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// name filtering, see Configuring Event Notifications (https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Filter *NotificationConfigurationFilter `type:"structure"`
|
||||
|
||||
|
@ -15332,7 +15332,7 @@ type LifecycleRule struct {
|
|||
NoncurrentVersionTransitions []*NoncurrentVersionTransition `locationName:"NoncurrentVersionTransition" type:"list" flattened:"true"`
|
||||
|
||||
// Prefix identifying one or more objects to which the rule applies. This is
|
||||
// deprecated; use Filter instead.
|
||||
// No longer used; use Filter instead.
|
||||
//
|
||||
// Deprecated: Prefix has been deprecated
|
||||
Prefix *string `deprecated:"true" type:"string"`
|
||||
|
@ -17624,8 +17624,8 @@ type NoncurrentVersionExpiration struct {
|
|||
// Specifies the number of days an object is noncurrent before Amazon S3 can
|
||||
// perform the associated action. For information about the noncurrent days
|
||||
// calculations, see How Amazon S3 Calculates When an Object Became Noncurrent
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html) in
|
||||
// the Amazon Simple Storage Service Developer Guide.
|
||||
// (https://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
NoncurrentDays *int64 `type:"integer"`
|
||||
}
|
||||
|
||||
|
@ -17646,19 +17646,20 @@ func (s *NoncurrentVersionExpiration) SetNoncurrentDays(v int64) *NoncurrentVers
|
|||
}
|
||||
|
||||
// Container for the transition rule that describes when noncurrent objects
|
||||
// transition to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING or GLACIER
|
||||
// storage class. If your bucket is versioning-enabled (or versioning is suspended),
|
||||
// you can set this action to request that Amazon S3 transition noncurrent object
|
||||
// versions to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING or GLACIER storage
|
||||
// class at a specific period in the object's lifetime.
|
||||
// transition to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER or
|
||||
// DEEP_ARCHIVE storage class. If your bucket is versioning-enabled (or versioning
|
||||
// is suspended), you can set this action to request that Amazon S3 transition
|
||||
// noncurrent object versions to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,
|
||||
// GLACIER or DEEP_ARCHIVE storage class at a specific period in the object's
|
||||
// lifetime.
|
||||
type NoncurrentVersionTransition struct {
|
||||
_ struct{} `type:"structure"`
|
||||
|
||||
// Specifies the number of days an object is noncurrent before Amazon S3 can
|
||||
// perform the associated action. For information about the noncurrent days
|
||||
// calculations, see How Amazon S3 Calculates When an Object Became Noncurrent
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html) in
|
||||
// the Amazon Simple Storage Service Developer Guide.
|
||||
// (https://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
NoncurrentDays *int64 `type:"integer"`
|
||||
|
||||
// The class of storage used to store the object.
|
||||
|
@ -17806,7 +17807,7 @@ func (s *NotificationConfigurationDeprecated) SetTopicConfiguration(v *TopicConf
|
|||
}
|
||||
|
||||
// A container for object key name filtering rules. For information about key
|
||||
// name filtering, see Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// name filtering, see Configuring Event Notifications (https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
type NotificationConfigurationFilter struct {
|
||||
_ struct{} `type:"structure"`
|
||||
|
@ -20199,7 +20200,8 @@ type PutObjectInput struct {
|
|||
// body cannot be determined automatically.
|
||||
ContentLength *int64 `location:"header" locationName:"Content-Length" type:"long"`
|
||||
|
||||
// The base64-encoded 128-bit MD5 digest of the part data.
|
||||
// The base64-encoded 128-bit MD5 digest of the part data. This parameter is
|
||||
// auto-populated when using the command from the CLI
|
||||
ContentMD5 *string `location:"header" locationName:"Content-MD5" type:"string"`
|
||||
|
||||
// A standard MIME type describing the format of the object data.
|
||||
|
@ -20638,6 +20640,7 @@ type PutObjectLockConfigurationInput struct {
|
|||
// at http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
|
||||
RequestPayer *string `location:"header" locationName:"x-amz-request-payer" type:"string" enum:"RequestPayer"`
|
||||
|
||||
// A token to allow Object Lock to be enabled for an existing bucket.
|
||||
Token *string `location:"header" locationName:"x-amz-bucket-object-lock-token" type:"string"`
|
||||
}
|
||||
|
||||
|
@ -21146,7 +21149,7 @@ type QueueConfiguration struct {
|
|||
Events []*string `locationName:"Event" type:"list" flattened:"true" required:"true"`
|
||||
|
||||
// A container for object key name filtering rules. For information about key
|
||||
// name filtering, see Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// name filtering, see Configuring Event Notifications (https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Filter *NotificationConfigurationFilter `type:"structure"`
|
||||
|
||||
|
@ -22120,11 +22123,12 @@ type Rule struct {
|
|||
NoncurrentVersionExpiration *NoncurrentVersionExpiration `type:"structure"`
|
||||
|
||||
// Container for the transition rule that describes when noncurrent objects
|
||||
// transition to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING or GLACIER
|
||||
// storage class. If your bucket is versioning-enabled (or versioning is suspended),
|
||||
// you can set this action to request that Amazon S3 transition noncurrent object
|
||||
// versions to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING or GLACIER storage
|
||||
// class at a specific period in the object's lifetime.
|
||||
// transition to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER or
|
||||
// DEEP_ARCHIVE storage class. If your bucket is versioning-enabled (or versioning
|
||||
// is suspended), you can set this action to request that Amazon S3 transition
|
||||
// noncurrent object versions to the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,
|
||||
// GLACIER or DEEP_ARCHIVE storage class at a specific period in the object's
|
||||
// lifetime.
|
||||
NoncurrentVersionTransition *NoncurrentVersionTransition `type:"structure"`
|
||||
|
||||
// Prefix identifying one or more objects to which the rule applies.
|
||||
|
@ -22496,7 +22500,7 @@ func (r *readSelectObjectContentEventStream) unmarshalerForEventType(
|
|||
// Amazon S3 uses this to parse object data into records. It returns only records
|
||||
// that match the specified SQL expression. You must also specify the data serialization
|
||||
// format for the response. For more information, see S3Select API Documentation
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectSELECTContent.html).
|
||||
// (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectSELECTContent.html).
|
||||
type SelectObjectContentInput struct {
|
||||
_ struct{} `locationName:"SelectObjectContentRequest" type:"structure" xmlURI:"http://s3.amazonaws.com/doc/2006-03-01/"`
|
||||
|
||||
|
@ -22534,15 +22538,15 @@ type SelectObjectContentInput struct {
|
|||
RequestProgress *RequestProgress `type:"structure"`
|
||||
|
||||
// The SSE Algorithm used to encrypt the object. For more information, see
|
||||
// Server-Side Encryption (Using Customer-Provided Encryption Keys (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
// Server-Side Encryption (Using Customer-Provided Encryption Keys (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
SSECustomerAlgorithm *string `location:"header" locationName:"x-amz-server-side-encryption-customer-algorithm" type:"string"`
|
||||
|
||||
// The SSE Customer Key. For more information, see Server-Side Encryption (Using
|
||||
// Customer-Provided Encryption Keys (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
// Customer-Provided Encryption Keys (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
SSECustomerKey *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key" type:"string" sensitive:"true"`
|
||||
|
||||
// The SSE Customer Key MD5. For more information, see Server-Side Encryption
|
||||
// (Using Customer-Provided Encryption Keys (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
// (Using Customer-Provided Encryption Keys (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
SSECustomerKeyMD5 *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key-MD5" type:"string"`
|
||||
}
|
||||
|
||||
|
@ -23347,7 +23351,7 @@ type TopicConfiguration struct {
|
|||
Events []*string `locationName:"Event" type:"list" flattened:"true" required:"true"`
|
||||
|
||||
// A container for object key name filtering rules. For information about key
|
||||
// name filtering, see Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// name filtering, see Configuring Event Notifications (https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Filter *NotificationConfigurationFilter `type:"structure"`
|
||||
|
||||
|
@ -23537,7 +23541,7 @@ type UploadPartCopyInput struct {
|
|||
// the form bytes=first-last, where the first and last are the zero-based byte
|
||||
// offsets to copy. For example, bytes=0-9 indicates that you want to copy the
|
||||
// first ten bytes of the source. You can copy a range only if the source object
|
||||
// is greater than 5 GB.
|
||||
// is greater than 5 MB.
|
||||
CopySourceRange *string `location:"header" locationName:"x-amz-copy-source-range" type:"string"`
|
||||
|
||||
// Specifies the algorithm to use when decrypting the source object (e.g., AES256).
|
||||
|
@ -24543,6 +24547,9 @@ const (
|
|||
|
||||
// ObjectStorageClassIntelligentTiering is a ObjectStorageClass enum value
|
||||
ObjectStorageClassIntelligentTiering = "INTELLIGENT_TIERING"
|
||||
|
||||
// ObjectStorageClassDeepArchive is a ObjectStorageClass enum value
|
||||
ObjectStorageClassDeepArchive = "DEEP_ARCHIVE"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -24673,6 +24680,9 @@ const (
|
|||
|
||||
// StorageClassGlacier is a StorageClass enum value
|
||||
StorageClassGlacier = "GLACIER"
|
||||
|
||||
// StorageClassDeepArchive is a StorageClass enum value
|
||||
StorageClassDeepArchive = "DEEP_ARCHIVE"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -24711,6 +24721,9 @@ const (
|
|||
|
||||
// TransitionStorageClassIntelligentTiering is a TransitionStorageClass enum value
|
||||
TransitionStorageClassIntelligentTiering = "INTELLIGENT_TIERING"
|
||||
|
||||
// TransitionStorageClassDeepArchive is a TransitionStorageClass enum value
|
||||
TransitionStorageClassDeepArchive = "DEEP_ARCHIVE"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -26,11 +26,16 @@ func unmarshalError(r *request.Request) {
|
|||
// Bucket exists in a different region, and request needs
|
||||
// to be made to the correct region.
|
||||
if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
|
||||
msg := fmt.Sprintf(
|
||||
"incorrect region, the bucket is not in '%s' region at endpoint '%s'",
|
||||
aws.StringValue(r.Config.Region),
|
||||
aws.StringValue(r.Config.Endpoint),
|
||||
)
|
||||
if v := r.HTTPResponse.Header.Get("x-amz-bucket-region"); len(v) != 0 {
|
||||
msg += fmt.Sprintf(", bucket is in '%s' region", v)
|
||||
}
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("BucketRegionError",
|
||||
fmt.Sprintf("incorrect region, the bucket is not in '%s' region",
|
||||
aws.StringValue(r.Config.Region)),
|
||||
nil),
|
||||
awserr.New("BucketRegionError", msg, nil),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
|
@ -243,6 +244,7 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re
|
|||
|
||||
output = &AssumeRoleWithSAMLOutput{}
|
||||
req = c.newRequest(op, input, output)
|
||||
req.Config.Credentials = credentials.AnonymousCredentials
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -425,6 +427,7 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI
|
|||
|
||||
output = &AssumeRoleWithWebIdentityOutput{}
|
||||
req = c.newRequest(op, input, output)
|
||||
req.Config.Credentials = credentials.AnonymousCredentials
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package sts
|
||||
|
||||
import "github.com/aws/aws-sdk-go/aws/request"
|
||||
|
||||
func init() {
|
||||
initRequest = func(r *request.Request) {
|
||||
switch r.Operation.Name {
|
||||
case opAssumeRoleWithSAML, opAssumeRoleWithWebIdentity:
|
||||
r.Handlers.Sign.Clear() // these operations are unsigned
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,6 +71,7 @@ can be augmented at runtime by implementing the `Getter` interface.
|
|||
* Mercurial
|
||||
* HTTP
|
||||
* Amazon S3
|
||||
* Google GCP
|
||||
|
||||
In addition to the above protocols, go-getter has what are called "detectors."
|
||||
These take a URL and attempt to automatically choose the best protocol for
|
||||
|
@ -279,6 +280,16 @@ None
|
|||
* `depth` - The Git clone depth. The provided number specifies the last `n`
|
||||
revisions to clone from the repository.
|
||||
|
||||
|
||||
The `git` getter accepts both URL-style SSH addresses like
|
||||
`git::ssh://git@example.com/foo/bar`, and "scp-style" addresses like
|
||||
`git::git@example.com/foo/bar`. In the latter case, omitting the `git::`
|
||||
force prefix is allowed if the username prefix is exactly `git@`.
|
||||
|
||||
The "scp-style" addresses _cannot_ be used in conjunction with the `ssh://`
|
||||
scheme prefix, because in that case the colon is used to mark an optional
|
||||
port number to connect on, rather than to delimit the path from the host.
|
||||
|
||||
### Mercurial (`hg`)
|
||||
|
||||
* `rev` - The Mercurial revision to checkout.
|
||||
|
@ -334,3 +345,14 @@ Some examples for these addressing schemes:
|
|||
- bucket.s3-eu-west-1.amazonaws.com/foo/bar
|
||||
- "s3::http://127.0.0.1:9000/test-bucket/hello.txt?aws_access_key_id=KEYID&aws_access_key_secret=SECRETKEY®ion=us-east-2"
|
||||
|
||||
### GCS (`gcs`)
|
||||
|
||||
#### GCS Authentication
|
||||
|
||||
In order to access to GCS, authentication credentials should be provided. More information can be found [here](https://cloud.google.com/docs/authentication/getting-started)
|
||||
|
||||
#### GCS Bucket Examples
|
||||
|
||||
- gcs::https://www.googleapis.com/storage/v1/bucket
|
||||
- gcs::https://www.googleapis.com/storage/v1/bucket/foo.zip
|
||||
- www.googleapis.com/storage/v1/bucket/foo
|
||||
|
|
|
@ -27,6 +27,27 @@ type fileChecksum struct {
|
|||
Filename string
|
||||
}
|
||||
|
||||
// A ChecksumError is returned when a checksum differs
|
||||
type ChecksumError struct {
|
||||
Hash hash.Hash
|
||||
Actual []byte
|
||||
Expected []byte
|
||||
File string
|
||||
}
|
||||
|
||||
func (cerr *ChecksumError) Error() string {
|
||||
if cerr == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"Checksums did not match for %s.\nExpected: %s\nGot: %s\n%T",
|
||||
cerr.File,
|
||||
hex.EncodeToString(cerr.Expected),
|
||||
hex.EncodeToString(cerr.Actual),
|
||||
cerr.Hash, // ex: *sha256.digest
|
||||
)
|
||||
}
|
||||
|
||||
// checksum is a simple method to compute the checksum of a source file
|
||||
// and compare it to the given expected value.
|
||||
func (c *fileChecksum) checksum(source string) error {
|
||||
|
@ -42,10 +63,12 @@ func (c *fileChecksum) checksum(source string) error {
|
|||
}
|
||||
|
||||
if actual := c.Hash.Sum(nil); !bytes.Equal(actual, c.Value) {
|
||||
return fmt.Errorf(
|
||||
"Checksums did not match.\nExpected: %s\nGot: %s",
|
||||
hex.EncodeToString(c.Value),
|
||||
hex.EncodeToString(actual))
|
||||
return &ChecksumError{
|
||||
Hash: c.Hash,
|
||||
Actual: actual,
|
||||
Expected: c.Value,
|
||||
File: source,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
// ZipDecompressor is an implementation of Decompressor that can
|
||||
// decompress tar.gzip files.
|
||||
// decompress zip files.
|
||||
type ZipDecompressor struct{}
|
||||
|
||||
func (d *ZipDecompressor) Decompress(dst, src string, dir bool) error {
|
||||
|
|
|
@ -26,6 +26,7 @@ func init() {
|
|||
new(GitDetector),
|
||||
new(BitBucketDetector),
|
||||
new(S3Detector),
|
||||
new(GCSDetector),
|
||||
new(FileDetector),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package getter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GCSDetector implements Detector to detect GCS URLs and turn
|
||||
// them into URLs that the GCSGetter can understand.
|
||||
type GCSDetector struct{}
|
||||
|
||||
func (d *GCSDetector) Detect(src, _ string) (string, bool, error) {
|
||||
if len(src) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
if strings.Contains(src, "googleapis.com/") {
|
||||
return d.detectHTTP(src)
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
func (d *GCSDetector) detectHTTP(src string) (string, bool, error) {
|
||||
|
||||
parts := strings.Split(src, "/")
|
||||
if len(parts) < 5 {
|
||||
return "", false, fmt.Errorf(
|
||||
"URL is not a valid GCS URL")
|
||||
}
|
||||
version := parts[2]
|
||||
bucket := parts[3]
|
||||
object := strings.Join(parts[4:], "/")
|
||||
|
||||
url, err := url.Parse(fmt.Sprintf("https://www.googleapis.com/storage/%s/%s/%s",
|
||||
version, bucket, object))
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("error parsing GCS URL: %s", err)
|
||||
}
|
||||
|
||||
return "gcs::" + url.String(), true, nil
|
||||
}
|
|
@ -67,6 +67,7 @@ func init() {
|
|||
Getters = map[string]Getter{
|
||||
"file": new(FileGetter),
|
||||
"git": new(GitGetter),
|
||||
"gcs": new(GCSGetter),
|
||||
"hg": new(HgGetter),
|
||||
"s3": new(S3Getter),
|
||||
"http": httpGetter,
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
package getter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"google.golang.org/api/iterator"
|
||||
)
|
||||
|
||||
// GCSGetter is a Getter implementation that will download a module from
|
||||
// a GCS bucket.
|
||||
type GCSGetter struct {
|
||||
getter
|
||||
}
|
||||
|
||||
func (g *GCSGetter) ClientMode(u *url.URL) (ClientMode, error) {
|
||||
ctx := g.Context()
|
||||
|
||||
// Parse URL
|
||||
bucket, object, err := g.parseURL(u)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iter := client.Bucket(bucket).Objects(ctx, &storage.Query{Prefix: object})
|
||||
for {
|
||||
obj, err := iter.Next()
|
||||
if err != nil && err != iterator.Done {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if strings.HasSuffix(obj.Name, "/") {
|
||||
// A directory matched the prefix search, so this must be a directory
|
||||
return ClientModeDir, nil
|
||||
} else if obj.Name != object {
|
||||
// A file matched the prefix search and doesn't have the same name
|
||||
// as the query, so this must be a directory
|
||||
return ClientModeDir, nil
|
||||
}
|
||||
}
|
||||
// There are no directories or subdirectories, and if a match was returned,
|
||||
// it was exactly equal to the prefix search. So return File mode
|
||||
return ClientModeFile, nil
|
||||
}
|
||||
|
||||
func (g *GCSGetter) Get(dst string, u *url.URL) error {
|
||||
ctx := g.Context()
|
||||
|
||||
// Parse URL
|
||||
bucket, object, err := g.parseURL(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove destination if it already exists
|
||||
_, err = os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// Remove the destination
|
||||
if err := os.RemoveAll(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create all the parent directories
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate through all matching objects.
|
||||
iter := client.Bucket(bucket).Objects(ctx, &storage.Query{Prefix: object})
|
||||
for {
|
||||
obj, err := iter.Next()
|
||||
if err != nil && err != iterator.Done {
|
||||
return err
|
||||
}
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(obj.Name, "/") {
|
||||
// Get the object destination path
|
||||
objDst, err := filepath.Rel(object, obj.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
objDst = filepath.Join(dst, objDst)
|
||||
// Download the matching object.
|
||||
err = g.getObject(ctx, client, objDst, bucket, obj.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GCSGetter) GetFile(dst string, u *url.URL) error {
|
||||
ctx := g.Context()
|
||||
|
||||
// Parse URL
|
||||
bucket, object, err := g.parseURL(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return g.getObject(ctx, client, dst, bucket, object)
|
||||
}
|
||||
|
||||
func (g *GCSGetter) getObject(ctx context.Context, client *storage.Client, dst, bucket, object string) error {
|
||||
rc, err := client.Bucket(bucket).Object(object).NewReader(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
// Create all the parent directories
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = Copy(ctx, f, rc)
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *GCSGetter) parseURL(u *url.URL) (bucket, path string, err error) {
|
||||
if strings.Contains(u.Host, "googleapis.com") {
|
||||
hostParts := strings.Split(u.Host, ".")
|
||||
if len(hostParts) != 3 {
|
||||
err = fmt.Errorf("URL is not a valid GCS URL")
|
||||
return
|
||||
}
|
||||
|
||||
pathParts := strings.SplitN(u.Path, "/", 5)
|
||||
if len(pathParts) != 5 {
|
||||
err = fmt.Errorf("URL is not a valid GCS URL")
|
||||
return
|
||||
}
|
||||
bucket = pathParts[3]
|
||||
path = pathParts[4]
|
||||
}
|
||||
return
|
||||
}
|
|
@ -34,6 +34,15 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
|||
return fmt.Errorf("git must be available and on the PATH")
|
||||
}
|
||||
|
||||
// The port number must be parseable as an integer. If not, the user
|
||||
// was probably trying to use a scp-style address, in which case the
|
||||
// ssh:// prefix must be removed to indicate that.
|
||||
if portStr := u.Port(); portStr != "" {
|
||||
if _, err := strconv.ParseUint(portStr, 10, 16); err != nil {
|
||||
return fmt.Errorf("invalid port number %q; if using the \"scp-like\" git address scheme where a colon introduces the path instead, remove the ssh:// portion and use just the git:: prefix", portStr)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract some query parameters we use
|
||||
var ref, sshKey string
|
||||
var depth int
|
||||
|
@ -90,26 +99,6 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
|||
}
|
||||
}
|
||||
|
||||
// For SSH-style URLs, if they use the SCP syntax of host:path, then
|
||||
// the URL will be mangled. We detect that here and correct the path.
|
||||
// Example: host:path/bar will turn into host/path/bar
|
||||
if u.Scheme == "ssh" {
|
||||
if idx := strings.Index(u.Host, ":"); idx > -1 {
|
||||
// Copy the URL so we don't modify the input
|
||||
var newU url.URL = *u
|
||||
u = &newU
|
||||
|
||||
// Path includes the part after the ':'.
|
||||
u.Path = u.Host[idx+1:] + u.Path
|
||||
if u.Path[0] != '/' {
|
||||
u.Path = "/" + u.Path
|
||||
}
|
||||
|
||||
// Host trims up to the :
|
||||
u.Host = u.Host[:idx]
|
||||
}
|
||||
}
|
||||
|
||||
// Clone or update the repository
|
||||
_, err := os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module github.com/hashicorp/go-getter
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.36.0
|
||||
github.com/aws/aws-sdk-go v1.15.78
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d
|
||||
github.com/cheggaaa/pb v1.0.27
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0
|
||||
github.com/hashicorp/go-safetemp v1.0.0
|
||||
|
@ -14,11 +14,9 @@ require (
|
|||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/mitchellh/go-homedir v1.0.0
|
||||
github.com/mitchellh/go-testing-interface v1.0.0
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2 // indirect
|
||||
github.com/ulikunitz/xz v0.5.5
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 // indirect
|
||||
golang.org/x/text v0.3.0 // indirect
|
||||
google.golang.org/api v0.1.0
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27 // indirect
|
||||
)
|
||||
|
|
|
@ -1,44 +1,182 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
||||
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
|
||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc=
|
||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
|
||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
|
|
@ -976,15 +976,15 @@ func (c *Client) logStderr(r io.Reader) {
|
|||
// Attempt to infer the desired log level from the commonly used
|
||||
// string prefixes
|
||||
switch line := string(line); {
|
||||
case strings.HasPrefix("[TRACE]", line):
|
||||
case strings.HasPrefix(line, "[TRACE]"):
|
||||
l.Trace(line)
|
||||
case strings.HasPrefix("[DEBUG]", line):
|
||||
case strings.HasPrefix(line, "[DEBUG]"):
|
||||
l.Debug(line)
|
||||
case strings.HasPrefix("[INFO]", line):
|
||||
case strings.HasPrefix(line, "[INFO]"):
|
||||
l.Info(line)
|
||||
case strings.HasPrefix("[WARN]", line):
|
||||
case strings.HasPrefix(line, "[WARN]"):
|
||||
l.Warn(line)
|
||||
case strings.HasPrefix("[ERROR]", line):
|
||||
case strings.HasPrefix(line, "[ERROR]"):
|
||||
l.Error(line)
|
||||
default:
|
||||
l.Debug(line)
|
||||
|
|
|
@ -213,6 +213,16 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks,
|
|||
diags = append(diags, blockDiags...)
|
||||
if block != nil {
|
||||
block.Body = b.expandChild(block.Body, i)
|
||||
|
||||
// We additionally force all of the leaf attribute values
|
||||
// in the result to be unknown so the calling application
|
||||
// can, if necessary, use that as a heuristic to detect
|
||||
// when a single nested block might be standing in for
|
||||
// multiple blocks yet to be expanded. This retains the
|
||||
// structure of the generated body but forces all of its
|
||||
// leaf attribute values to be unknown.
|
||||
block.Body = unknownBody{block.Body}
|
||||
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// unknownBody is a funny body that just reports everything inside it as
|
||||
// unknown. It uses a given other body as a sort of template for what attributes
|
||||
// and blocks are inside -- including source location information -- but
|
||||
// subsitutes unknown values of unknown type for all attributes.
|
||||
//
|
||||
// This rather odd process is used to handle expansion of dynamic blocks whose
|
||||
// for_each expression is unknown. Since a block cannot itself be unknown,
|
||||
// we instead arrange for everything _inside_ the block to be unknown instead,
|
||||
// to give the best possible approximation.
|
||||
type unknownBody struct {
|
||||
template hcl.Body
|
||||
}
|
||||
|
||||
var _ hcl.Body = unknownBody{}
|
||||
|
||||
func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
|
||||
content, diags := b.template.Content(schema)
|
||||
content = b.fixupContent(content)
|
||||
|
||||
// We're intentionally preserving the diagnostics reported from the
|
||||
// inner body so that we can still report where the template body doesn't
|
||||
// match the requested schema.
|
||||
return content, diags
|
||||
}
|
||||
|
||||
func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
|
||||
content, remain, diags := b.template.PartialContent(schema)
|
||||
content = b.fixupContent(content)
|
||||
remain = unknownBody{remain} // remaining content must also be wrapped
|
||||
|
||||
// We're intentionally preserving the diagnostics reported from the
|
||||
// inner body so that we can still report where the template body doesn't
|
||||
// match the requested schema.
|
||||
return content, remain, diags
|
||||
}
|
||||
|
||||
func (b unknownBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
||||
attrs, diags := b.template.JustAttributes()
|
||||
attrs = b.fixupAttrs(attrs)
|
||||
|
||||
// We're intentionally preserving the diagnostics reported from the
|
||||
// inner body so that we can still report where the template body doesn't
|
||||
// match the requested schema.
|
||||
return attrs, diags
|
||||
}
|
||||
|
||||
func (b unknownBody) MissingItemRange() hcl.Range {
|
||||
return b.template.MissingItemRange()
|
||||
}
|
||||
|
||||
func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent {
|
||||
ret := &hcl.BodyContent{}
|
||||
ret.Attributes = b.fixupAttrs(got.Attributes)
|
||||
if len(got.Blocks) > 0 {
|
||||
ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks))
|
||||
for _, gotBlock := range got.Blocks {
|
||||
new := *gotBlock // shallow copy
|
||||
new.Body = unknownBody{gotBlock.Body} // nested content must also be marked unknown
|
||||
ret.Blocks = append(ret.Blocks, &new)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes {
|
||||
if len(got) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make(hcl.Attributes, len(got))
|
||||
for name, gotAttr := range got {
|
||||
new := *gotAttr // shallow copy
|
||||
new.Expr = hcl.StaticExpr(cty.DynamicVal, gotAttr.Expr.Range())
|
||||
ret[name] = &new
|
||||
}
|
||||
return ret
|
||||
}
|
|
@ -417,6 +417,17 @@ Token:
|
|||
p.recoverAfterBodyItem()
|
||||
}
|
||||
|
||||
// We must never produce a nil body, since the caller may attempt to
|
||||
// do analysis of a partial result when there's an error, so we'll
|
||||
// insert a placeholder if we otherwise failed to produce a valid
|
||||
// body due to one of the syntax error paths above.
|
||||
if body == nil && diags.HasErrors() {
|
||||
body = &Body{
|
||||
SrcRange: hcl.RangeBetween(oBrace.Range, cBraceRange),
|
||||
EndRange: cBraceRange,
|
||||
}
|
||||
}
|
||||
|
||||
return &Block{
|
||||
Type: blockType,
|
||||
Labels: labels,
|
||||
|
|
|
@ -2,10 +2,10 @@ package hclsyntax
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/apparentlymart/go-textseg/textseg"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/apparentlymart/go-textseg/textseg"
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -63,8 +63,16 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To
|
|||
BeginHeredocTmpl = '<<' ('-')? Ident Newline;
|
||||
|
||||
Comment = (
|
||||
("#" (any - EndOfLine)* EndOfLine) |
|
||||
("//" (any - EndOfLine)* EndOfLine) |
|
||||
# The :>> operator in these is a "finish-guarded concatenation",
|
||||
# which terminates the sequence on its left when it completes
|
||||
# the sequence on its right.
|
||||
# In the single-line comment cases this is allowing us to make
|
||||
# the trailing EndOfLine optional while still having the overall
|
||||
# pattern terminate. In the multi-line case it ensures that
|
||||
# the first comment in the file ends at the first */, rather than
|
||||
# gobbling up all of the "any*" until the _final_ */ in the file.
|
||||
("#" (any - EndOfLine)* :>> EndOfLine?) |
|
||||
("//" (any - EndOfLine)* :>> EndOfLine?) |
|
||||
("/*" any* :>> "*/")
|
||||
);
|
||||
|
||||
|
@ -218,29 +226,35 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To
|
|||
TemplateInterp = "${" ("~")?;
|
||||
TemplateControl = "%{" ("~")?;
|
||||
EndStringTmpl = '"';
|
||||
StringLiteralChars = (AnyUTF8 - ("\r"|"\n"));
|
||||
NewlineChars = ("\r"|"\n");
|
||||
NewlineCharsSeq = NewlineChars+;
|
||||
StringLiteralChars = (AnyUTF8 - NewlineChars);
|
||||
TemplateIgnoredNonBrace = (^'{' %{ fhold; });
|
||||
TemplateNotInterp = '$' (TemplateIgnoredNonBrace | TemplateInterp);
|
||||
TemplateNotControl = '%' (TemplateIgnoredNonBrace | TemplateControl);
|
||||
QuotedStringLiteralWithEsc = ('\\' StringLiteralChars) | (StringLiteralChars - ("$" | '%' | '"' | "\\"));
|
||||
TemplateStringLiteral = (
|
||||
('$' ^'{' %{ fhold; }) |
|
||||
('%' ^'{' %{ fhold; }) |
|
||||
('\\' StringLiteralChars) |
|
||||
(StringLiteralChars - ("$" | '%' | '"'))
|
||||
)+;
|
||||
(TemplateNotInterp) |
|
||||
(TemplateNotControl) |
|
||||
(QuotedStringLiteralWithEsc)+
|
||||
);
|
||||
HeredocStringLiteral = (
|
||||
('$' ^'{' %{ fhold; }) |
|
||||
('%' ^'{' %{ fhold; }) |
|
||||
(StringLiteralChars - ("$" | '%'))
|
||||
)*;
|
||||
(TemplateNotInterp) |
|
||||
(TemplateNotControl) |
|
||||
(StringLiteralChars - ("$" | '%'))*
|
||||
);
|
||||
BareStringLiteral = (
|
||||
('$' ^'{') |
|
||||
('%' ^'{') |
|
||||
(StringLiteralChars - ("$" | '%'))
|
||||
)* Newline?;
|
||||
(TemplateNotInterp) |
|
||||
(TemplateNotControl) |
|
||||
(StringLiteralChars - ("$" | '%'))*
|
||||
) Newline?;
|
||||
|
||||
stringTemplate := |*
|
||||
TemplateInterp => beginTemplateInterp;
|
||||
TemplateControl => beginTemplateControl;
|
||||
EndStringTmpl => endStringTemplate;
|
||||
TemplateStringLiteral => { token(TokenQuotedLit); };
|
||||
NewlineCharsSeq => { token(TokenQuotedNewline); };
|
||||
AnyUTF8 => { token(TokenInvalid); };
|
||||
BrokenUTF8 => { token(TokenBadUTF8); };
|
||||
*|;
|
||||
|
|
|
@ -85,17 +85,18 @@ const (
|
|||
// things that might work in other languages they are familiar with, or
|
||||
// simply make incorrect assumptions about the HCL language.
|
||||
|
||||
TokenBitwiseAnd TokenType = '&'
|
||||
TokenBitwiseOr TokenType = '|'
|
||||
TokenBitwiseNot TokenType = '~'
|
||||
TokenBitwiseXor TokenType = '^'
|
||||
TokenStarStar TokenType = '➚'
|
||||
TokenApostrophe TokenType = '\''
|
||||
TokenBacktick TokenType = '`'
|
||||
TokenSemicolon TokenType = ';'
|
||||
TokenTabs TokenType = '␉'
|
||||
TokenInvalid TokenType = '<27>'
|
||||
TokenBadUTF8 TokenType = '💩'
|
||||
TokenBitwiseAnd TokenType = '&'
|
||||
TokenBitwiseOr TokenType = '|'
|
||||
TokenBitwiseNot TokenType = '~'
|
||||
TokenBitwiseXor TokenType = '^'
|
||||
TokenStarStar TokenType = '➚'
|
||||
TokenApostrophe TokenType = '\''
|
||||
TokenBacktick TokenType = '`'
|
||||
TokenSemicolon TokenType = ';'
|
||||
TokenTabs TokenType = '␉'
|
||||
TokenInvalid TokenType = '<27>'
|
||||
TokenBadUTF8 TokenType = '💩'
|
||||
TokenQuotedNewline TokenType = ''
|
||||
|
||||
// TokenNil is a placeholder for when a token is required but none is
|
||||
// available, e.g. when reporting errors. The scanner will never produce
|
||||
|
@ -285,6 +286,13 @@ func checkInvalidTokens(tokens Tokens) hcl.Diagnostics {
|
|||
|
||||
toldBadUTF8++
|
||||
}
|
||||
case TokenQuotedNewline:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid multi-line string",
|
||||
Detail: "Quoted strings may not be split over multiple lines. To produce a multi-line string, either use the \\n escape to represent a newline character or use the \"heredoc\" multi-line template syntax.",
|
||||
Subject: &tok.Range,
|
||||
})
|
||||
case TokenInvalid:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
|
|
@ -4,7 +4,67 @@ package hclsyntax
|
|||
|
||||
import "strconv"
|
||||
|
||||
const _TokenType_name = "TokenNilTokenNewlineTokenBangTokenPercentTokenBitwiseAndTokenApostropheTokenOParenTokenCParenTokenStarTokenPlusTokenCommaTokenMinusTokenDotTokenSlashTokenColonTokenSemicolonTokenLessThanTokenEqualTokenGreaterThanTokenQuestionTokenCommentTokenOHeredocTokenIdentTokenNumberLitTokenQuotedLitTokenStringLitTokenOBrackTokenCBrackTokenBitwiseXorTokenBacktickTokenCHeredocTokenOBraceTokenBitwiseOrTokenCBraceTokenBitwiseNotTokenOQuoteTokenCQuoteTokenTemplateControlTokenEllipsisTokenFatArrowTokenTemplateSeqEndTokenAndTokenOrTokenTemplateInterpTokenEqualOpTokenNotEqualTokenLessThanEqTokenGreaterThanEqTokenEOFTokenTabsTokenStarStarTokenInvalidTokenBadUTF8"
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[TokenOBrace-123]
|
||||
_ = x[TokenCBrace-125]
|
||||
_ = x[TokenOBrack-91]
|
||||
_ = x[TokenCBrack-93]
|
||||
_ = x[TokenOParen-40]
|
||||
_ = x[TokenCParen-41]
|
||||
_ = x[TokenOQuote-171]
|
||||
_ = x[TokenCQuote-187]
|
||||
_ = x[TokenOHeredoc-72]
|
||||
_ = x[TokenCHeredoc-104]
|
||||
_ = x[TokenStar-42]
|
||||
_ = x[TokenSlash-47]
|
||||
_ = x[TokenPlus-43]
|
||||
_ = x[TokenMinus-45]
|
||||
_ = x[TokenPercent-37]
|
||||
_ = x[TokenEqual-61]
|
||||
_ = x[TokenEqualOp-8788]
|
||||
_ = x[TokenNotEqual-8800]
|
||||
_ = x[TokenLessThan-60]
|
||||
_ = x[TokenLessThanEq-8804]
|
||||
_ = x[TokenGreaterThan-62]
|
||||
_ = x[TokenGreaterThanEq-8805]
|
||||
_ = x[TokenAnd-8743]
|
||||
_ = x[TokenOr-8744]
|
||||
_ = x[TokenBang-33]
|
||||
_ = x[TokenDot-46]
|
||||
_ = x[TokenComma-44]
|
||||
_ = x[TokenEllipsis-8230]
|
||||
_ = x[TokenFatArrow-8658]
|
||||
_ = x[TokenQuestion-63]
|
||||
_ = x[TokenColon-58]
|
||||
_ = x[TokenTemplateInterp-8747]
|
||||
_ = x[TokenTemplateControl-955]
|
||||
_ = x[TokenTemplateSeqEnd-8718]
|
||||
_ = x[TokenQuotedLit-81]
|
||||
_ = x[TokenStringLit-83]
|
||||
_ = x[TokenNumberLit-78]
|
||||
_ = x[TokenIdent-73]
|
||||
_ = x[TokenComment-67]
|
||||
_ = x[TokenNewline-10]
|
||||
_ = x[TokenEOF-9220]
|
||||
_ = x[TokenBitwiseAnd-38]
|
||||
_ = x[TokenBitwiseOr-124]
|
||||
_ = x[TokenBitwiseNot-126]
|
||||
_ = x[TokenBitwiseXor-94]
|
||||
_ = x[TokenStarStar-10138]
|
||||
_ = x[TokenApostrophe-39]
|
||||
_ = x[TokenBacktick-96]
|
||||
_ = x[TokenSemicolon-59]
|
||||
_ = x[TokenTabs-9225]
|
||||
_ = x[TokenInvalid-65533]
|
||||
_ = x[TokenBadUTF8-128169]
|
||||
_ = x[TokenQuotedNewline-9252]
|
||||
_ = x[TokenNil-0]
|
||||
}
|
||||
|
||||
const _TokenType_name = "TokenNilTokenNewlineTokenBangTokenPercentTokenBitwiseAndTokenApostropheTokenOParenTokenCParenTokenStarTokenPlusTokenCommaTokenMinusTokenDotTokenSlashTokenColonTokenSemicolonTokenLessThanTokenEqualTokenGreaterThanTokenQuestionTokenCommentTokenOHeredocTokenIdentTokenNumberLitTokenQuotedLitTokenStringLitTokenOBrackTokenCBrackTokenBitwiseXorTokenBacktickTokenCHeredocTokenOBraceTokenBitwiseOrTokenCBraceTokenBitwiseNotTokenOQuoteTokenCQuoteTokenTemplateControlTokenEllipsisTokenFatArrowTokenTemplateSeqEndTokenAndTokenOrTokenTemplateInterpTokenEqualOpTokenNotEqualTokenLessThanEqTokenGreaterThanEqTokenEOFTokenTabsTokenQuotedNewlineTokenStarStarTokenInvalidTokenBadUTF8"
|
||||
|
||||
var _TokenType_map = map[TokenType]string{
|
||||
0: _TokenType_name[0:8],
|
||||
|
@ -57,9 +117,10 @@ var _TokenType_map = map[TokenType]string{
|
|||
8805: _TokenType_name[577:595],
|
||||
9220: _TokenType_name[595:603],
|
||||
9225: _TokenType_name[603:612],
|
||||
10138: _TokenType_name[612:625],
|
||||
65533: _TokenType_name[625:637],
|
||||
128169: _TokenType_name[637:649],
|
||||
9252: _TokenType_name[612:630],
|
||||
10138: _TokenType_name[630:643],
|
||||
65533: _TokenType_name[643:655],
|
||||
128169: _TokenType_name[655:667],
|
||||
}
|
||||
|
||||
func (i TokenType) String() string {
|
||||
|
|
|
@ -153,7 +153,7 @@ func byteCanStartKeyword(b byte) bool {
|
|||
// in the parser, where we can generate better diagnostics.
|
||||
// So e.g. we want to be able to say:
|
||||
// unrecognized keyword "True". Did you mean "true"?
|
||||
case b >= 'a' || b <= 'z' || b >= 'A' || b <= 'Z':
|
||||
case isAlphabetical(b):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -167,7 +167,7 @@ Byte:
|
|||
for i = 0; i < len(buf); i++ {
|
||||
b := buf[i]
|
||||
switch {
|
||||
case (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_':
|
||||
case isAlphabetical(b) || b == '_':
|
||||
p.Pos.Byte++
|
||||
p.Pos.Column++
|
||||
default:
|
||||
|
@ -291,3 +291,7 @@ func posRange(start, end pos) hcl.Range {
|
|||
func (t token) GoString() string {
|
||||
return fmt.Sprintf("json.token{json.%s, []byte(%q), %#v}", t.Type, t.Bytes, t.Range)
|
||||
}
|
||||
|
||||
func isAlphabetical(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
|
||||
}
|
||||
|
|
|
@ -248,8 +248,8 @@ func spaceAfterToken(subject, before, after *Token) bool {
|
|||
// Don't use spaces around attribute access dots
|
||||
return false
|
||||
|
||||
case after.Type == hclsyntax.TokenComma:
|
||||
// No space right before a comma in an argument list
|
||||
case after.Type == hclsyntax.TokenComma || after.Type == hclsyntax.TokenEllipsis:
|
||||
// No space right before a comma or ... in an argument list
|
||||
return false
|
||||
|
||||
case subject.Type == hclsyntax.TokenComma:
|
||||
|
|
|
@ -4,6 +4,15 @@ package addrs
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[InvalidResourceMode-0]
|
||||
_ = x[ManagedResourceMode-77]
|
||||
_ = x[DataResourceMode-68]
|
||||
}
|
||||
|
||||
const (
|
||||
_ResourceMode_name_0 = "InvalidResourceMode"
|
||||
_ResourceMode_name_1 = "DataResourceMode"
|
||||
|
|
|
@ -1101,8 +1101,8 @@ func ctySequenceDiff(old, new []cty.Value) []*plans.Change {
|
|||
if lcsI < len(lcs) {
|
||||
ret = append(ret, &plans.Change{
|
||||
Action: plans.NoOp,
|
||||
Before: new[newI],
|
||||
After: new[newI],
|
||||
Before: lcs[lcsI],
|
||||
After: lcs[lcsI],
|
||||
})
|
||||
|
||||
// All of our indexes advance together now, since the line
|
||||
|
|
|
@ -4,6 +4,14 @@ package config
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ManagedResourceMode-0]
|
||||
_ = x[DataResourceMode-1]
|
||||
}
|
||||
|
||||
const _ResourceMode_name = "ManagedResourceModeDataResourceMode"
|
||||
|
||||
var _ResourceMode_index = [...]uint8{0, 19, 35}
|
||||
|
|
|
@ -4,6 +4,15 @@ package configs
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ProvisionerOnFailureInvalid-0]
|
||||
_ = x[ProvisionerOnFailureContinue-1]
|
||||
_ = x[ProvisionerOnFailureFail-2]
|
||||
}
|
||||
|
||||
const _ProvisionerOnFailure_name = "ProvisionerOnFailureInvalidProvisionerOnFailureContinueProvisionerOnFailureFail"
|
||||
|
||||
var _ProvisionerOnFailure_index = [...]uint8{0, 27, 55, 79}
|
||||
|
|
|
@ -4,6 +4,15 @@ package configs
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ProvisionerWhenInvalid-0]
|
||||
_ = x[ProvisionerWhenCreate-1]
|
||||
_ = x[ProvisionerWhenDestroy-2]
|
||||
}
|
||||
|
||||
const _ProvisionerWhen_name = "ProvisionerWhenInvalidProvisionerWhenCreateProvisionerWhenDestroy"
|
||||
|
||||
var _ProvisionerWhen_index = [...]uint8{0, 22, 43, 65}
|
||||
|
|
|
@ -4,6 +4,16 @@ package configs
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[TypeHintNone-0]
|
||||
_ = x[TypeHintString-83]
|
||||
_ = x[TypeHintList-76]
|
||||
_ = x[TypeHintMap-77]
|
||||
}
|
||||
|
||||
const (
|
||||
_VariableTypeHint_name_0 = "TypeHintNone"
|
||||
_VariableTypeHint_name_1 = "TypeHintListTypeHintMap"
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
@ -51,7 +52,7 @@ func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProvider
|
|||
}
|
||||
|
||||
resp.Provider = &proto.Schema{
|
||||
Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlockForCore()),
|
||||
Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()),
|
||||
}
|
||||
|
||||
for typ, res := range s.provider.ResourcesMap {
|
||||
|
@ -71,46 +72,26 @@ func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProvider
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getProviderSchemaBlockForCore() *configschema.Block {
|
||||
func (s *GRPCProviderServer) getProviderSchemaBlock() *configschema.Block {
|
||||
return schema.InternalMap(s.provider.Schema).CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getResourceSchemaBlockForCore(name string) *configschema.Block {
|
||||
func (s *GRPCProviderServer) getResourceSchemaBlock(name string) *configschema.Block {
|
||||
res := s.provider.ResourcesMap[name]
|
||||
return res.CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getDatasourceSchemaBlockForCore(name string) *configschema.Block {
|
||||
func (s *GRPCProviderServer) getDatasourceSchemaBlock(name string) *configschema.Block {
|
||||
dat := s.provider.DataSourcesMap[name]
|
||||
return dat.CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getProviderSchemaBlockForShimming() *configschema.Block {
|
||||
newSchema := map[string]*schema.Schema{}
|
||||
for attr, s := range s.provider.Schema {
|
||||
newSchema[attr] = schema.LegacySchema(s)
|
||||
}
|
||||
|
||||
return schema.InternalMap(newSchema).CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getResourceSchemaBlockForShimming(name string) *configschema.Block {
|
||||
res := s.provider.ResourcesMap[name]
|
||||
return schema.LegacyResourceSchema(res).CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getDatasourceSchemaBlockForShimming(name string) *configschema.Block {
|
||||
dat := s.provider.DataSourcesMap[name]
|
||||
return schema.LegacyResourceSchema(dat).CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto.PrepareProviderConfig_Request) (*proto.PrepareProviderConfig_Response, error) {
|
||||
resp := &proto.PrepareProviderConfig_Response{}
|
||||
|
||||
blockForCore := s.getProviderSchemaBlockForCore()
|
||||
blockForShimming := s.getProviderSchemaBlockForShimming()
|
||||
schemaBlock := s.getProviderSchemaBlock()
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -181,7 +162,7 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
configVal, err = blockForShimming.CoerceValue(configVal)
|
||||
configVal, err = schemaBlock.CoerceValue(configVal)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -193,12 +174,12 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, schemaBlock)
|
||||
|
||||
warns, errs := s.provider.Validate(config)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
||||
preparedConfigMP, err := msgpack.Marshal(configVal, blockForCore.ImpliedType())
|
||||
preparedConfigMP, err := msgpack.Marshal(configVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -212,16 +193,15 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
|||
func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *proto.ValidateResourceTypeConfig_Request) (*proto.ValidateResourceTypeConfig_Response, error) {
|
||||
resp := &proto.ValidateResourceTypeConfig_Response{}
|
||||
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getResourceSchemaBlock(req.TypeName)
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, schemaBlock)
|
||||
|
||||
warns, errs := s.provider.ValidateResource(req.TypeName, config)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
@ -232,10 +212,9 @@ func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *
|
|||
func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *proto.ValidateDataSourceConfig_Request) (*proto.ValidateDataSourceConfig_Response, error) {
|
||||
resp := &proto.ValidateDataSourceConfig_Response{}
|
||||
|
||||
blockForCore := s.getDatasourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getDatasourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getDatasourceSchemaBlock(req.TypeName)
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -247,7 +226,7 @@ func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *pr
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, schemaBlock)
|
||||
|
||||
warns, errs := s.provider.ValidateDataSource(req.TypeName, config)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
@ -259,31 +238,32 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
resp := &proto.UpgradeResourceState_Response{}
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getResourceSchemaBlock(req.TypeName)
|
||||
|
||||
version := int(req.Version)
|
||||
|
||||
var jsonMap map[string]interface{}
|
||||
jsonMap := map[string]interface{}{}
|
||||
var err error
|
||||
|
||||
// if there's a JSON state, we need to decode it.
|
||||
if len(req.RawState.Json) > 0 {
|
||||
err = json.Unmarshal(req.RawState.Json, &jsonMap)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
// We first need to upgrade a flatmap state if it exists.
|
||||
// There should never be both a JSON and Flatmap state in the request.
|
||||
if req.RawState.Flatmap != nil {
|
||||
case len(req.RawState.Flatmap) > 0:
|
||||
jsonMap, version, err = s.upgradeFlatmapState(version, req.RawState.Flatmap, res)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
// if there's a JSON state, we need to decode it.
|
||||
case len(req.RawState.Json) > 0:
|
||||
err = json.Unmarshal(req.RawState.Json, &jsonMap)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
default:
|
||||
log.Println("[DEBUG] no state provided to upgrade")
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// complete the upgrade of the JSON states
|
||||
|
@ -293,16 +273,19 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// The provider isn't required to clean out removed fields
|
||||
s.removeAttributes(jsonMap, schemaBlock.ImpliedType())
|
||||
|
||||
// now we need to turn the state into the default json representation, so
|
||||
// that it can be re-decoded using the actual schema.
|
||||
val, err := schema.JSONMapToStateValue(jsonMap, blockForShimming)
|
||||
val, err := schema.JSONMapToStateValue(jsonMap, schemaBlock)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// encode the final state to the expected msgpack format
|
||||
newStateMP, err := msgpack.Marshal(val, blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(val, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -325,7 +308,7 @@ func (s *GRPCProviderServer) upgradeFlatmapState(version int, m map[string]strin
|
|||
// first determine if we need to call the legacy MigrateState func
|
||||
requiresMigrate := version < res.SchemaVersion
|
||||
|
||||
schemaType := schema.LegacyResourceSchema(res).CoreConfigSchema().ImpliedType()
|
||||
schemaType := res.CoreConfigSchema().ImpliedType()
|
||||
|
||||
// if there are any StateUpgraders, then we need to only compare
|
||||
// against the first version there
|
||||
|
@ -404,6 +387,57 @@ func (s *GRPCProviderServer) upgradeJSONState(version int, m map[string]interfac
|
|||
return m, nil
|
||||
}
|
||||
|
||||
// Remove any attributes no longer present in the schema, so that the json can
|
||||
// be correctly decoded.
|
||||
func (s *GRPCProviderServer) removeAttributes(v interface{}, ty cty.Type) {
|
||||
// we're only concerned with finding maps that corespond to object
|
||||
// attributes
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
// If these aren't blocks the next call will be a noop
|
||||
if ty.IsListType() || ty.IsSetType() {
|
||||
eTy := ty.ElementType()
|
||||
for _, eV := range v {
|
||||
s.removeAttributes(eV, eTy)
|
||||
}
|
||||
}
|
||||
return
|
||||
case map[string]interface{}:
|
||||
// map blocks aren't yet supported, but handle this just in case
|
||||
if ty.IsMapType() {
|
||||
eTy := ty.ElementType()
|
||||
for _, eV := range v {
|
||||
s.removeAttributes(eV, eTy)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ty == cty.DynamicPseudoType {
|
||||
log.Printf("[DEBUG] ignoring dynamic block: %#v\n", v)
|
||||
return
|
||||
}
|
||||
|
||||
if !ty.IsObjectType() {
|
||||
// This shouldn't happen, and will fail to decode further on, so
|
||||
// there's no need to handle it here.
|
||||
log.Printf("[WARN] unexpected type %#v for map in json state", ty)
|
||||
return
|
||||
}
|
||||
|
||||
attrTypes := ty.AttributeTypes()
|
||||
for attr, attrV := range v {
|
||||
attrTy, ok := attrTypes[attr]
|
||||
if !ok {
|
||||
log.Printf("[DEBUG] attribute %q no longer present in schema", attr)
|
||||
delete(v, attr)
|
||||
continue
|
||||
}
|
||||
|
||||
s.removeAttributes(attrV, attrTy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) Stop(_ context.Context, _ *proto.Stop_Request) (*proto.Stop_Response, error) {
|
||||
resp := &proto.Stop_Response{}
|
||||
|
||||
|
@ -418,10 +452,9 @@ func (s *GRPCProviderServer) Stop(_ context.Context, _ *proto.Stop_Request) (*pr
|
|||
func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_Request) (*proto.Configure_Response, error) {
|
||||
resp := &proto.Configure_Response{}
|
||||
|
||||
blockForCore := s.getProviderSchemaBlockForCore()
|
||||
blockForShimming := s.getProviderSchemaBlockForShimming()
|
||||
schemaBlock := s.getProviderSchemaBlock()
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -435,7 +468,7 @@ func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_R
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, schemaBlock)
|
||||
err = s.provider.Configure(config)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
|
||||
|
@ -446,10 +479,9 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
resp := &proto.ReadResource_Response{}
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getResourceSchemaBlock(req.TypeName)
|
||||
|
||||
stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, blockForCore.ImpliedType())
|
||||
stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -471,7 +503,7 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
// The old provider API used an empty id to signal that the remote
|
||||
// object appears to have been deleted, but our new protocol expects
|
||||
// to see a null value (in the cty sense) in that case.
|
||||
newStateMP, err := msgpack.Marshal(cty.NullVal(blockForCore.ImpliedType()), blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(cty.NullVal(schemaBlock.ImpliedType()), schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
}
|
||||
|
@ -484,16 +516,16 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
// helper/schema should always copy the ID over, but do it again just to be safe
|
||||
newInstanceState.Attributes["id"] = newInstanceState.ID
|
||||
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, blockForShimming.ImpliedType())
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateVal = normalizeNullValues(newStateVal, stateVal, true)
|
||||
newStateVal = normalizeNullValues(newStateVal, stateVal, false)
|
||||
newStateVal = copyTimeoutValues(newStateVal, stateVal)
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -518,10 +550,9 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
resp.LegacyTypeSystem = true
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getResourceSchemaBlock(req.TypeName)
|
||||
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, blockForCore.ImpliedType())
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -529,7 +560,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
|
||||
create := priorStateVal.IsNull()
|
||||
|
||||
proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, blockForCore.ImpliedType())
|
||||
proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -567,7 +598,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
}
|
||||
|
||||
// turn the proposed state into a legacy configuration
|
||||
cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, blockForShimming)
|
||||
cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, schemaBlock)
|
||||
|
||||
diff, err := s.provider.SimpleDiff(info, priorState, cfg)
|
||||
if err != nil {
|
||||
|
@ -600,21 +631,21 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
}
|
||||
|
||||
// now we need to apply the diff to the prior state, so get the planned state
|
||||
plannedAttrs, err := diff.Apply(priorState.Attributes, blockForShimming)
|
||||
plannedAttrs, err := diff.Apply(priorState.Attributes, schemaBlock)
|
||||
|
||||
plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, blockForShimming.ImpliedType())
|
||||
plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedStateVal, err = blockForShimming.CoerceValue(plannedStateVal)
|
||||
plannedStateVal, err = schemaBlock.CoerceValue(plannedStateVal)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedStateVal = normalizeNullValues(plannedStateVal, proposedNewStateVal, true)
|
||||
plannedStateVal = normalizeNullValues(plannedStateVal, proposedNewStateVal, false)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
|
@ -640,10 +671,10 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
// if this was creating the resource, we need to set any remaining computed
|
||||
// fields
|
||||
if create {
|
||||
plannedStateVal = SetUnknowns(plannedStateVal, blockForShimming)
|
||||
plannedStateVal = SetUnknowns(plannedStateVal, schemaBlock)
|
||||
}
|
||||
|
||||
plannedMP, err := msgpack.Marshal(plannedStateVal, blockForCore.ImpliedType())
|
||||
plannedMP, err := msgpack.Marshal(plannedStateVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -696,7 +727,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
requiresNew = append(requiresNew, "id")
|
||||
}
|
||||
|
||||
requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, blockForShimming.ImpliedType())
|
||||
requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -717,16 +748,15 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
}
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getResourceSchemaBlock(req.TypeName)
|
||||
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, blockForCore.ImpliedType())
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, blockForCore.ImpliedType())
|
||||
plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -814,13 +844,13 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
}
|
||||
newStateVal := cty.NullVal(blockForShimming.ImpliedType())
|
||||
newStateVal := cty.NullVal(schemaBlock.ImpliedType())
|
||||
|
||||
// Always return a null value for destroy.
|
||||
// While this is usually indicated by a nil state, check for missing ID or
|
||||
// attributes in the case of a provider failure.
|
||||
if destroy || newInstanceState == nil || newInstanceState.Attributes == nil || newInstanceState.ID == "" {
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -833,17 +863,17 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
|
||||
// We keep the null val if we destroyed the resource, otherwise build the
|
||||
// entire object, even if the new state was nil.
|
||||
newStateVal, err = schema.StateValueFromInstanceState(newInstanceState, blockForShimming.ImpliedType())
|
||||
newStateVal, err = schema.StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateVal = normalizeNullValues(newStateVal, plannedStateVal, false)
|
||||
newStateVal = normalizeNullValues(newStateVal, plannedStateVal, true)
|
||||
|
||||
newStateVal = copyTimeoutValues(newStateVal, plannedStateVal)
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -892,15 +922,14 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
|||
resourceType = req.TypeName
|
||||
}
|
||||
|
||||
blockForCore := s.getResourceSchemaBlockForCore(resourceType)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(resourceType)
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, blockForShimming.ImpliedType())
|
||||
schemaBlock := s.getResourceSchemaBlock(resourceType)
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -929,10 +958,9 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
|||
func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDataSource_Request) (*proto.ReadDataSource_Response, error) {
|
||||
resp := &proto.ReadDataSource_Response{}
|
||||
|
||||
blockForCore := s.getDatasourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getDatasourceSchemaBlockForShimming(req.TypeName)
|
||||
schemaBlock := s.getDatasourceSchemaBlock(req.TypeName)
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -948,7 +976,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, schemaBlock)
|
||||
|
||||
// we need to still build the diff separately with the Read method to match
|
||||
// the old behavior
|
||||
|
@ -965,7 +993,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, blockForShimming.ImpliedType())
|
||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -973,7 +1001,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
|
||||
newStateVal = copyTimeoutValues(newStateVal, configVal)
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -1108,33 +1136,27 @@ func stripSchema(s *schema.Schema) *schema.Schema {
|
|||
// however it sees fit. This however means that a CustomizeDiffFunction may not
|
||||
// be able to change a null to an empty value or vice versa, but that should be
|
||||
// very uncommon nor was it reliable before 0.12 either.
|
||||
func normalizeNullValues(dst, src cty.Value, preferDst bool) cty.Value {
|
||||
func normalizeNullValues(dst, src cty.Value, apply bool) cty.Value {
|
||||
ty := dst.Type()
|
||||
|
||||
if !src.IsNull() && !src.IsKnown() {
|
||||
// While this seems backwards to return src when preferDst is set, it
|
||||
// means this might be a plan scenario, and it must retain unknown
|
||||
// interpolated placeholders, which could be lost if we're only updating
|
||||
// a resource. If this is a read scenario, then there shouldn't be any
|
||||
// unknowns all.
|
||||
if dst.IsNull() && preferDst {
|
||||
// Return src during plan to retain unknown interpolated placeholders,
|
||||
// which could be lost if we're only updating a resource. If this is a
|
||||
// read scenario, then there shouldn't be any unknowns at all.
|
||||
if dst.IsNull() && !apply {
|
||||
return src
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// handle null/empty changes for collections
|
||||
if ty.IsCollectionType() {
|
||||
if src.IsNull() && !dst.IsNull() && dst.IsKnown() {
|
||||
if dst.LengthInt() == 0 {
|
||||
return src
|
||||
}
|
||||
}
|
||||
// Handle null/empty changes for collections during apply.
|
||||
// A change between null and empty values prefers src to make sure the state
|
||||
// is consistent between plan and apply.
|
||||
if ty.IsCollectionType() && apply {
|
||||
dstEmpty := !dst.IsNull() && dst.IsKnown() && dst.LengthInt() == 0
|
||||
srcEmpty := !src.IsNull() && src.IsKnown() && src.LengthInt() == 0
|
||||
|
||||
if dst.IsNull() && !src.IsNull() && src.IsKnown() {
|
||||
if src.LengthInt() == 0 {
|
||||
return src
|
||||
}
|
||||
if (src.IsNull() && dstEmpty) || (srcEmpty && dst.IsNull()) {
|
||||
return src
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1154,22 +1176,28 @@ func normalizeNullValues(dst, src cty.Value, preferDst bool) cty.Value {
|
|||
|
||||
srcMap := src.AsValueMap()
|
||||
for key, v := range srcMap {
|
||||
dstVal := dstMap[key]
|
||||
dstVal, ok := dstMap[key]
|
||||
if !ok && apply && ty.IsMapType() {
|
||||
// don't transfer old map values to dst during apply
|
||||
continue
|
||||
}
|
||||
|
||||
if dstVal == cty.NilVal {
|
||||
if preferDst && ty.IsMapType() {
|
||||
if !apply && ty.IsMapType() {
|
||||
// let plan shape this map however it wants
|
||||
continue
|
||||
}
|
||||
dstVal = cty.NullVal(v.Type())
|
||||
}
|
||||
dstMap[key] = normalizeNullValues(dstVal, v, preferDst)
|
||||
|
||||
dstMap[key] = normalizeNullValues(dstVal, v, apply)
|
||||
}
|
||||
|
||||
// you can't call MapVal/ObjectVal with empty maps, but nothing was
|
||||
// copied in anyway. If the dst is nil, and the src is known, assume the
|
||||
// src is correct.
|
||||
if len(dstMap) == 0 {
|
||||
if dst.IsNull() && src.IsWhollyKnown() && !preferDst {
|
||||
if dst.IsNull() && src.IsWhollyKnown() && apply {
|
||||
return src
|
||||
}
|
||||
return dst
|
||||
|
@ -1203,7 +1231,7 @@ func normalizeNullValues(dst, src cty.Value, preferDst bool) cty.Value {
|
|||
// If the original was wholly known, then we expect that is what the
|
||||
// provider applied. The apply process loses too much information to
|
||||
// reliably re-create the set.
|
||||
if src.IsWhollyKnown() && !preferDst {
|
||||
if src.IsWhollyKnown() && apply {
|
||||
return src
|
||||
}
|
||||
|
||||
|
@ -1211,14 +1239,13 @@ func normalizeNullValues(dst, src cty.Value, preferDst bool) cty.Value {
|
|||
// If the dst is null, and the src is known, then we lost an empty value
|
||||
// so take the original.
|
||||
if dst.IsNull() {
|
||||
if src.IsWhollyKnown() && src.LengthInt() == 0 && !preferDst {
|
||||
if src.IsWhollyKnown() && src.LengthInt() == 0 && apply {
|
||||
return src
|
||||
}
|
||||
|
||||
// if dst is null and src only contains unknown values, then we lost
|
||||
// those during a plan (which is when preferDst is set, there would
|
||||
// be no unknowns during read).
|
||||
if preferDst && !src.IsNull() {
|
||||
// those during a read or plan.
|
||||
if !apply && !src.IsNull() {
|
||||
allUnknown := true
|
||||
for _, v := range src.AsValueSlice() {
|
||||
if v.IsKnown() {
|
||||
|
@ -1242,7 +1269,7 @@ func normalizeNullValues(dst, src cty.Value, preferDst bool) cty.Value {
|
|||
dsts := dst.AsValueSlice()
|
||||
|
||||
for i := 0; i < srcLen; i++ {
|
||||
dsts[i] = normalizeNullValues(dsts[i], srcs[i], preferDst)
|
||||
dsts[i] = normalizeNullValues(dsts[i], srcs[i], apply)
|
||||
}
|
||||
|
||||
if ty.IsTupleType() {
|
||||
|
@ -1252,7 +1279,7 @@ func normalizeNullValues(dst, src cty.Value, preferDst bool) cty.Value {
|
|||
}
|
||||
|
||||
case ty.IsPrimitiveType():
|
||||
if dst.IsNull() && src.IsWhollyKnown() && !preferDst {
|
||||
if dst.IsNull() && src.IsWhollyKnown() && apply {
|
||||
return src
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func GRPCTestProvider(rp terraform.ResourceProvider) providers.Interface {
|
|||
client, _ := pp.GRPCClient(context.Background(), nil, conn)
|
||||
|
||||
grpcClient := client.(*tfplugin.GRPCProvider)
|
||||
grpcClient.TestListener = listener
|
||||
grpcClient.TestServer = grpcServer
|
||||
|
||||
return grpcClient
|
||||
}
|
||||
|
|
|
@ -174,14 +174,6 @@ func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
|
|||
// coreConfigSchemaType determines the core config schema type that corresponds
|
||||
// to a particular schema's type.
|
||||
func (s *Schema) coreConfigSchemaType() cty.Type {
|
||||
if s.SkipCoreTypeCheck {
|
||||
// If we're preparing a schema for Terraform Core and the schema is
|
||||
// asking us to skip the Core type-check then we'll tell core that this
|
||||
// attribute is dynamically-typed, so it'll just pass through anything
|
||||
// and let us validate it on the plugin side.
|
||||
return cty.DynamicPseudoType
|
||||
}
|
||||
|
||||
switch s.Type {
|
||||
case TypeString:
|
||||
return cty.String
|
||||
|
|
|
@ -2,6 +2,7 @@ package schema
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -93,6 +94,22 @@ func (r *ConfigFieldReader) readField(
|
|||
}
|
||||
}
|
||||
|
||||
if protoVersion5 {
|
||||
switch schema.Type {
|
||||
case TypeList, TypeSet, TypeMap, typeObject:
|
||||
// Check if the value itself is unknown.
|
||||
// The new protocol shims will add unknown values to this list of
|
||||
// ComputedKeys. This is the only way we have to indicate that a
|
||||
// collection is unknown in the config
|
||||
for _, unknown := range r.Config.ComputedKeys {
|
||||
if k == unknown {
|
||||
log.Printf("[DEBUG] setting computed for %q from ComputedKeys", k)
|
||||
return FieldReadResult{Computed: true, Exists: true}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch schema.Type {
|
||||
case TypeBool, TypeFloat, TypeInt, TypeString:
|
||||
return r.readPrimitive(k, schema)
|
||||
|
|
|
@ -4,6 +4,18 @@ package schema
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[getSourceState-1]
|
||||
_ = x[getSourceConfig-2]
|
||||
_ = x[getSourceDiff-4]
|
||||
_ = x[getSourceSet-8]
|
||||
_ = x[getSourceExact-16]
|
||||
_ = x[getSourceLevelMask-15]
|
||||
}
|
||||
|
||||
const (
|
||||
_getSource_name_0 = "getSourceStategetSourceConfig"
|
||||
_getSource_name_1 = "getSourceDiff"
|
||||
|
|
|
@ -95,34 +95,6 @@ type Schema struct {
|
|||
// behavior, and SchemaConfigModeBlock is not permitted.
|
||||
ConfigMode SchemaConfigMode
|
||||
|
||||
// SkipCoreTypeCheck, if set, will advertise this attribute to Terraform Core
|
||||
// has being dynamically-typed rather than deriving a type from the schema.
|
||||
// This has the effect of making Terraform Core skip all type-checking of
|
||||
// the value, and thus leaves all type checking up to a combination of this
|
||||
// SDK and the provider's own code.
|
||||
//
|
||||
// This flag does nothing for Terraform versions prior to v0.12, because
|
||||
// in prior versions there was no Core-side typecheck anyway.
|
||||
//
|
||||
// The most practical effect of this flag is to allow object-typed schemas
|
||||
// (specified with Elem: schema.Resource) to pass through Terraform Core
|
||||
// even without all of the object type attributes specified, which may be
|
||||
// useful when using ConfigMode: SchemaConfigModeAttr to achieve
|
||||
// nested-block-like behaviors while using attribute syntax.
|
||||
//
|
||||
// However, by doing so we require type information to be sent and stored
|
||||
// per-object rather than just once statically in the schema, and so this
|
||||
// will change the wire serialization of a resource type in state. Changing
|
||||
// the value of SkipCoreTypeCheck will therefore require a state migration
|
||||
// if there has previously been any release of the provider compatible with
|
||||
// Terraform v0.12.
|
||||
//
|
||||
// SkipCoreTypeCheck can only be set when ConfigMode is SchemaConfigModeAttr,
|
||||
// because nested blocks cannot be decoded by Terraform Core without at
|
||||
// least shallow information about the next level of nested attributes and
|
||||
// blocks.
|
||||
SkipCoreTypeCheck bool
|
||||
|
||||
// If one of these is set, then this item can come from the configuration.
|
||||
// Both cannot be set. If Optional is set, the value is optional. If
|
||||
// Required is set, the value is required.
|
||||
|
@ -735,8 +707,6 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
|||
|
||||
computedOnly := v.Computed && !v.Optional
|
||||
|
||||
isBlock := false
|
||||
|
||||
switch v.ConfigMode {
|
||||
case SchemaConfigModeBlock:
|
||||
if _, ok := v.Elem.(*Resource); !ok {
|
||||
|
@ -748,7 +718,6 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
|||
if computedOnly {
|
||||
return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k)
|
||||
}
|
||||
isBlock = true
|
||||
case SchemaConfigModeAttr:
|
||||
// anything goes
|
||||
case SchemaConfigModeAuto:
|
||||
|
@ -756,7 +725,6 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
|||
// and that's impossible inside an attribute, we require it to be
|
||||
// explicitly overridden as mode "Attr" for clarity.
|
||||
if _, ok := v.Elem.(*Resource); ok {
|
||||
isBlock = true
|
||||
if attrsOnly {
|
||||
return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k)
|
||||
}
|
||||
|
@ -765,10 +733,6 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
|||
return fmt.Errorf("%s: invalid ConfigMode value", k)
|
||||
}
|
||||
|
||||
if isBlock && v.SkipCoreTypeCheck {
|
||||
return fmt.Errorf("%s: SkipCoreTypeCheck must be false unless ConfigMode is attribute", k)
|
||||
}
|
||||
|
||||
if v.Computed && v.Default != nil {
|
||||
return fmt.Errorf("%s: Default must be nil if computed", k)
|
||||
}
|
||||
|
@ -1731,12 +1695,25 @@ func (m schemaMap) validatePrimitive(
|
|||
}
|
||||
decoded = n
|
||||
case TypeInt:
|
||||
// Verify that we can parse this as an int
|
||||
var n int
|
||||
if err := mapstructure.WeakDecode(raw, &n); err != nil {
|
||||
return nil, []error{fmt.Errorf("%s: %s", k, err)}
|
||||
switch {
|
||||
case isProto5():
|
||||
// We need to verify the type precisely, because WeakDecode will
|
||||
// decode a float as an integer.
|
||||
|
||||
// the config shims only use int for integral number values
|
||||
if v, ok := raw.(int); ok {
|
||||
decoded = v
|
||||
} else {
|
||||
return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)}
|
||||
}
|
||||
default:
|
||||
// Verify that we can parse this as an int
|
||||
var n int
|
||||
if err := mapstructure.WeakDecode(raw, &n); err != nil {
|
||||
return nil, []error{fmt.Errorf("%s: %s", k, err)}
|
||||
}
|
||||
decoded = n
|
||||
}
|
||||
decoded = n
|
||||
case TypeFloat:
|
||||
// Verify that we can parse this as an int
|
||||
var n float64
|
||||
|
|
|
@ -51,7 +51,7 @@ func removeConfigUnknowns(cfg map[string]interface{}) {
|
|||
switch v := v.(type) {
|
||||
case string:
|
||||
if v == config.UnknownVariableValue {
|
||||
cfg[k] = ""
|
||||
delete(cfg, k)
|
||||
}
|
||||
case []interface{}:
|
||||
for _, i := range v {
|
||||
|
@ -113,45 +113,3 @@ func JSONMapToStateValue(m map[string]interface{}, block *configschema.Block) (c
|
|||
func StateValueFromInstanceState(is *terraform.InstanceState, ty cty.Type) (cty.Value, error) {
|
||||
return is.AttrsAsObjectValue(ty)
|
||||
}
|
||||
|
||||
// LegacyResourceSchema takes a *Resource and returns a deep copy with 0.12 specific
|
||||
// features removed. This is used by the shims to get a configschema that
|
||||
// directly matches the structure of the schema.Resource.
|
||||
func LegacyResourceSchema(r *Resource) *Resource {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
// start with a shallow copy
|
||||
newResource := new(Resource)
|
||||
*newResource = *r
|
||||
newResource.Schema = map[string]*Schema{}
|
||||
|
||||
for k, s := range r.Schema {
|
||||
newResource.Schema[k] = LegacySchema(s)
|
||||
}
|
||||
|
||||
return newResource
|
||||
}
|
||||
|
||||
// LegacySchema takes a *Schema and returns a deep copy with some 0.12-specific
|
||||
// features disabled. This is used by the shims to get a configschema that
|
||||
// better reflects the given schema.Resource, without any adjustments we
|
||||
// make for when sending a schema to Terraform Core.
|
||||
func LegacySchema(s *Schema) *Schema {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
// start with a shallow copy
|
||||
newSchema := new(Schema)
|
||||
*newSchema = *s
|
||||
newSchema.SkipCoreTypeCheck = false
|
||||
|
||||
switch e := newSchema.Elem.(type) {
|
||||
case *Schema:
|
||||
newSchema.Elem = LegacySchema(e)
|
||||
case *Resource:
|
||||
newSchema.Elem = LegacyResourceSchema(e)
|
||||
}
|
||||
|
||||
return newSchema
|
||||
}
|
||||
|
|
|
@ -4,6 +4,21 @@ package schema
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[TypeInvalid-0]
|
||||
_ = x[TypeBool-1]
|
||||
_ = x[TypeInt-2]
|
||||
_ = x[TypeFloat-3]
|
||||
_ = x[TypeString-4]
|
||||
_ = x[TypeList-5]
|
||||
_ = x[TypeMap-6]
|
||||
_ = x[TypeSet-7]
|
||||
_ = x[typeObject-8]
|
||||
}
|
||||
|
||||
const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject"
|
||||
|
||||
var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77}
|
||||
|
|
|
@ -320,3 +320,22 @@ func ValidateRFC3339TimeString(v interface{}, k string) (ws []string, errors []e
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FloatBetween returns a SchemaValidateFunc which tests if the provided value
|
||||
// is of type float64 and is between min and max (inclusive).
|
||||
func FloatBetween(min, max float64) schema.SchemaValidateFunc {
|
||||
return func(i interface{}, k string) (s []string, es []error) {
|
||||
v, ok := i.(float64)
|
||||
if !ok {
|
||||
es = append(es, fmt.Errorf("expected type of %s to be float64", k))
|
||||
return
|
||||
}
|
||||
|
||||
if v < min || v > max {
|
||||
es = append(es, fmt.Errorf("expected %s to be in the range (%f - %f), got %f", k, min, max, v))
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package initwd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform/registry"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -13,6 +12,7 @@ import (
|
|||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/internal/earlyconfig"
|
||||
"github.com/hashicorp/terraform/internal/modsdir"
|
||||
"github.com/hashicorp/terraform/registry"
|
||||
"github.com/hashicorp/terraform/registry/regsrc"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
@ -245,7 +245,18 @@ func (i *ModuleInstaller) installLocalModule(req *earlyconfig.ModuleRequest, key
|
|||
// filesystem at all because the parent already wrote
|
||||
// the files we need, and so we just load up what's already here.
|
||||
newDir := filepath.Join(parentRecord.Dir, req.SourceAddr)
|
||||
|
||||
log.Printf("[TRACE] ModuleInstaller: %s uses directory from parent: %s", key, newDir)
|
||||
// it is possible that the local directory is a symlink
|
||||
newDir, err := filepath.EvalSymlinks(newDir)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Unreadable module directory",
|
||||
fmt.Sprintf("Unable to evaluate directory symlink: %s", err.Error()),
|
||||
))
|
||||
}
|
||||
|
||||
mod, mDiags := earlyconfig.LoadModule(newDir)
|
||||
if mod == nil {
|
||||
// nil indicates missing or unreadable directory, so we'll
|
||||
|
@ -417,7 +428,7 @@ func (i *ModuleInstaller) installRegistryModule(req *earlyconfig.ModuleRequest,
|
|||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to download module",
|
||||
fmt.Sprintf("Error attempting to download module %q (%s:%d) source code from %q: %s.", req.Name, req.CallPos.Filename, req.CallPos.Line, dlAddr, err),
|
||||
fmt.Sprintf("Could not download module %q (%s:%d) source code from %q: %s.", req.Name, req.CallPos.Filename, req.CallPos.Line, dlAddr, err),
|
||||
))
|
||||
return nil, nil, diags
|
||||
}
|
||||
|
@ -469,9 +480,18 @@ func (i *ModuleInstaller) installGoGetterModule(req *earlyconfig.ModuleRequest,
|
|||
packageAddr, _ := splitAddrSubdir(req.SourceAddr)
|
||||
hooks.Download(key, packageAddr, nil)
|
||||
|
||||
if len(req.VersionConstraints) != 0 {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid version constraint",
|
||||
fmt.Sprintf("Cannot apply a version constraint to module %q (at %s:%d) because it has a non Registry URL.", req.Name, req.CallPos.Filename, req.CallPos.Line),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
modDir, err := getter.getWithGoGetter(instPath, req.SourceAddr)
|
||||
if err != nil {
|
||||
if err, ok := err.(*MaybeRelativePathErr); ok {
|
||||
if _, ok := err.(*MaybeRelativePathErr); ok {
|
||||
log.Printf(
|
||||
"[TRACE] ModuleInstaller: %s looks like a local path but is missing ./ or ../",
|
||||
req.SourceAddr,
|
||||
|
@ -496,7 +516,7 @@ func (i *ModuleInstaller) installGoGetterModule(req *earlyconfig.ModuleRequest,
|
|||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to download module",
|
||||
fmt.Sprintf("Error attempting to download module %q (%s:%d) source code from %q: %s", req.Name, req.CallPos.Filename, req.CallPos.Line, packageAddr, err),
|
||||
fmt.Sprintf("Could not download module %q (%s:%d) source code from %q: %s", req.Name, req.CallPos.Filename, req.CallPos.Line, packageAddr, err),
|
||||
))
|
||||
}
|
||||
return nil, diags
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package funcs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
|
@ -43,7 +44,7 @@ var ElementFunc = function.New(&function.Spec{
|
|||
return cty.DynamicPseudoType, fmt.Errorf("invalid index: %s", err)
|
||||
}
|
||||
if len(etys) == 0 {
|
||||
return cty.DynamicPseudoType, fmt.Errorf("cannot use element function with an empty list")
|
||||
return cty.DynamicPseudoType, errors.New("cannot use element function with an empty list")
|
||||
}
|
||||
index = index % len(etys)
|
||||
return etys[index], nil
|
||||
|
@ -65,7 +66,7 @@ var ElementFunc = function.New(&function.Spec{
|
|||
|
||||
l := args[0].LengthInt()
|
||||
if l == 0 {
|
||||
return cty.DynamicVal, fmt.Errorf("cannot use element function with an empty list")
|
||||
return cty.DynamicVal, errors.New("cannot use element function with an empty list")
|
||||
}
|
||||
index = index % l
|
||||
|
||||
|
@ -90,7 +91,7 @@ var LengthFunc = function.New(&function.Spec{
|
|||
case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType:
|
||||
return cty.Number, nil
|
||||
default:
|
||||
return cty.Number, fmt.Errorf("argument must be a string, a collection type, or a structural type")
|
||||
return cty.Number, errors.New("argument must be a string, a collection type, or a structural type")
|
||||
}
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
|
@ -114,12 +115,12 @@ var LengthFunc = function.New(&function.Spec{
|
|||
return coll.Length(), nil
|
||||
default:
|
||||
// Should never happen, because of the checks in our Type func above
|
||||
return cty.UnknownVal(cty.Number), fmt.Errorf("impossible value type for length(...)")
|
||||
return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)")
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// CoalesceFunc contructs a function that takes any number of arguments and
|
||||
// CoalesceFunc constructs a function that takes any number of arguments and
|
||||
// returns the first one that isn't empty. This function was copied from go-cty
|
||||
// stdlib and modified so that it returns the first *non-empty* non-null element
|
||||
// from a sequence, instead of merely the first non-null.
|
||||
|
@ -139,7 +140,7 @@ var CoalesceFunc = function.New(&function.Spec{
|
|||
}
|
||||
retType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if retType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("all arguments must have the same type")
|
||||
return cty.NilType, errors.New("all arguments must have the same type")
|
||||
}
|
||||
return retType, nil
|
||||
},
|
||||
|
@ -159,42 +160,53 @@ var CoalesceFunc = function.New(&function.Spec{
|
|||
|
||||
return argVal, nil
|
||||
}
|
||||
return cty.NilVal, fmt.Errorf("no non-null, non-empty-string arguments")
|
||||
return cty.NilVal, errors.New("no non-null, non-empty-string arguments")
|
||||
},
|
||||
})
|
||||
|
||||
// CoalesceListFunc contructs a function that takes any number of list arguments
|
||||
// CoalesceListFunc constructs a function that takes any number of list arguments
|
||||
// and returns the first one that isn't empty.
|
||||
var CoalesceListFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "vals",
|
||||
Type: cty.List(cty.DynamicPseudoType),
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowUnknown: true,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
if len(args) == 0 {
|
||||
return cty.NilType, fmt.Errorf("at least one argument is required")
|
||||
return cty.NilType, errors.New("at least one argument is required")
|
||||
}
|
||||
|
||||
argTypes := make([]cty.Type, len(args))
|
||||
|
||||
for i, arg := range args {
|
||||
// if any argument is unknown, we can't be certain know which type we will return
|
||||
if !arg.IsKnown() {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
ty := arg.Type()
|
||||
|
||||
if !ty.IsListType() && !ty.IsTupleType() {
|
||||
return cty.NilType, errors.New("coalescelist arguments must be lists or tuples")
|
||||
}
|
||||
|
||||
argTypes[i] = arg.Type()
|
||||
}
|
||||
|
||||
retType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if retType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("all arguments must have the same type")
|
||||
last := argTypes[0]
|
||||
// If there are mixed types, we have to return a dynamic type.
|
||||
for _, next := range argTypes[1:] {
|
||||
if !next.Equals(last) {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
}
|
||||
|
||||
return retType, nil
|
||||
return last, nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
|
||||
vals := make([]cty.Value, 0, len(args))
|
||||
for _, arg := range args {
|
||||
if !arg.IsKnown() {
|
||||
// If we run into an unknown list at some point, we can't
|
||||
|
@ -203,25 +215,16 @@ var CoalesceListFunc = function.New(&function.Spec{
|
|||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
// We already know this will succeed because of the checks in our Type func above
|
||||
arg, _ = convert.Convert(arg, retType)
|
||||
|
||||
it := arg.ElementIterator()
|
||||
for it.Next() {
|
||||
_, v := it.Element()
|
||||
vals = append(vals, v)
|
||||
}
|
||||
|
||||
if len(vals) > 0 {
|
||||
return cty.ListVal(vals), nil
|
||||
if arg.LengthInt() > 0 {
|
||||
return arg, nil
|
||||
}
|
||||
}
|
||||
|
||||
return cty.NilVal, fmt.Errorf("no non-null arguments")
|
||||
return cty.NilVal, errors.New("no non-null arguments")
|
||||
},
|
||||
})
|
||||
|
||||
// CompactFunc contructs a function that takes a list of strings and returns a new list
|
||||
// CompactFunc constructs a function that takes a list of strings and returns a new list
|
||||
// with any empty string elements removed.
|
||||
var CompactFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
|
@ -257,13 +260,13 @@ var CompactFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// ContainsFunc contructs a function that determines whether a given list contains
|
||||
// a given single value as one of its elements.
|
||||
// ContainsFunc constructs a function that determines whether a given list or
|
||||
// set contains a given single value as one of its elements.
|
||||
var ContainsFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "list",
|
||||
Type: cty.List(cty.DynamicPseudoType),
|
||||
Type: cty.DynamicPseudoType,
|
||||
},
|
||||
{
|
||||
Name: "value",
|
||||
|
@ -272,8 +275,14 @@ var ContainsFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
arg := args[0]
|
||||
ty := arg.Type()
|
||||
|
||||
_, err = Index(args[0], args[1])
|
||||
if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() {
|
||||
return cty.NilVal, errors.New("argument must be list, tuple, or set")
|
||||
}
|
||||
|
||||
_, err = Index(cty.TupleVal(arg.AsValueSlice()), args[1])
|
||||
if err != nil {
|
||||
return cty.False, nil
|
||||
}
|
||||
|
@ -282,7 +291,7 @@ var ContainsFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// IndexFunc contructs a function that finds the element index for a given value in a list.
|
||||
// IndexFunc constructs a function that finds the element index for a given value in a list.
|
||||
var IndexFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
|
@ -297,7 +306,7 @@ var IndexFunc = function.New(&function.Spec{
|
|||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) {
|
||||
return cty.NilVal, fmt.Errorf("argument must be a list or tuple")
|
||||
return cty.NilVal, errors.New("argument must be a list or tuple")
|
||||
}
|
||||
|
||||
if !args[0].IsKnown() {
|
||||
|
@ -305,7 +314,7 @@ var IndexFunc = function.New(&function.Spec{
|
|||
}
|
||||
|
||||
if args[0].LengthInt() == 0 { // Easy path
|
||||
return cty.NilVal, fmt.Errorf("cannot search an empty list")
|
||||
return cty.NilVal, errors.New("cannot search an empty list")
|
||||
}
|
||||
|
||||
for it := args[0].ElementIterator(); it.Next(); {
|
||||
|
@ -321,12 +330,12 @@ var IndexFunc = function.New(&function.Spec{
|
|||
return i, nil
|
||||
}
|
||||
}
|
||||
return cty.NilVal, fmt.Errorf("item not found")
|
||||
return cty.NilVal, errors.New("item not found")
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
// DistinctFunc contructs a function that takes a list and returns a new list
|
||||
// DistinctFunc constructs a function that takes a list and returns a new list
|
||||
// with any duplicate elements removed.
|
||||
var DistinctFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
|
@ -358,7 +367,7 @@ var DistinctFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// ChunklistFunc contructs a function that splits a single list into fixed-size chunks,
|
||||
// ChunklistFunc constructs a function that splits a single list into fixed-size chunks,
|
||||
// returning a list of lists.
|
||||
var ChunklistFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
|
@ -376,7 +385,7 @@ var ChunklistFunc = function.New(&function.Spec{
|
|||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
listVal := args[0]
|
||||
if !listVal.IsWhollyKnown() {
|
||||
if !listVal.IsKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
|
@ -387,7 +396,7 @@ var ChunklistFunc = function.New(&function.Spec{
|
|||
}
|
||||
|
||||
if size < 0 {
|
||||
return cty.NilVal, fmt.Errorf("the size argument must be positive")
|
||||
return cty.NilVal, errors.New("the size argument must be positive")
|
||||
}
|
||||
|
||||
output := make([]cty.Value, 0)
|
||||
|
@ -419,47 +428,76 @@ var ChunklistFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// FlattenFunc contructs a function that takes a list and replaces any elements
|
||||
// FlattenFunc constructs a function that takes a list and replaces any elements
|
||||
// that are lists with a flattened sequence of the list contents.
|
||||
var FlattenFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "list",
|
||||
Type: cty.List(cty.DynamicPseudoType),
|
||||
Type: cty.DynamicPseudoType,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.DynamicPseudoType)),
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
if !args[0].IsWhollyKnown() {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
argTy := args[0].Type()
|
||||
if !argTy.IsListType() && !argTy.IsSetType() && !argTy.IsTupleType() {
|
||||
return cty.NilType, errors.New("can only flatten lists, sets and tuples")
|
||||
}
|
||||
|
||||
retVal, known := flattener(args[0])
|
||||
if !known {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
tys := make([]cty.Type, len(retVal))
|
||||
for i, ty := range retVal {
|
||||
tys[i] = ty.Type()
|
||||
}
|
||||
return cty.Tuple(tys), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
inputList := args[0]
|
||||
if !inputList.IsWhollyKnown() {
|
||||
if inputList.LengthInt() == 0 {
|
||||
return cty.EmptyTupleVal, nil
|
||||
}
|
||||
|
||||
out, known := flattener(inputList)
|
||||
if !known {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if inputList.LengthInt() == 0 {
|
||||
return cty.ListValEmpty(retType.ElementType()), nil
|
||||
}
|
||||
outputList := make([]cty.Value, 0)
|
||||
|
||||
return cty.ListVal(flattener(outputList, inputList)), nil
|
||||
return cty.TupleVal(out), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Flatten until it's not a cty.List
|
||||
func flattener(finalList []cty.Value, flattenList cty.Value) []cty.Value {
|
||||
|
||||
// Flatten until it's not a cty.List, and return whether the value is known.
|
||||
// We can flatten lists with unknown values, as long as they are not
|
||||
// lists themselves.
|
||||
func flattener(flattenList cty.Value) ([]cty.Value, bool) {
|
||||
out := make([]cty.Value, 0)
|
||||
for it := flattenList.ElementIterator(); it.Next(); {
|
||||
_, val := it.Element()
|
||||
if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() {
|
||||
if !val.IsKnown() {
|
||||
return out, false
|
||||
}
|
||||
|
||||
if val.Type().IsListType() {
|
||||
finalList = flattener(finalList, val)
|
||||
res, known := flattener(val)
|
||||
if !known {
|
||||
return res, known
|
||||
}
|
||||
out = append(out, res...)
|
||||
} else {
|
||||
finalList = append(finalList, val)
|
||||
out = append(out, val)
|
||||
}
|
||||
}
|
||||
return finalList
|
||||
return out, true
|
||||
}
|
||||
|
||||
// KeysFunc contructs a function that takes a map and returns a sorted list of the map keys.
|
||||
// KeysFunc constructs a function that takes a map and returns a sorted list of the map keys.
|
||||
var KeysFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
|
@ -529,7 +567,7 @@ var KeysFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// ListFunc contructs a function that takes an arbitrary number of arguments
|
||||
// ListFunc constructs a function that takes an arbitrary number of arguments
|
||||
// and returns a list containing those values in the same order.
|
||||
//
|
||||
// This function is deprecated in Terraform v0.12
|
||||
|
@ -544,7 +582,7 @@ var ListFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
if len(args) == 0 {
|
||||
return cty.NilType, fmt.Errorf("at least one argument is required")
|
||||
return cty.NilType, errors.New("at least one argument is required")
|
||||
}
|
||||
|
||||
argTypes := make([]cty.Type, len(args))
|
||||
|
@ -555,7 +593,7 @@ var ListFunc = function.New(&function.Spec{
|
|||
|
||||
retType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if retType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("all arguments must have the same type")
|
||||
return cty.NilType, errors.New("all arguments must have the same type")
|
||||
}
|
||||
|
||||
return cty.List(retType), nil
|
||||
|
@ -573,7 +611,7 @@ var ListFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// LookupFunc contructs a function that performs dynamic lookups of map types.
|
||||
// LookupFunc constructs a function that performs dynamic lookups of map types.
|
||||
var LookupFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
|
@ -649,7 +687,7 @@ var LookupFunc = function.New(&function.Spec{
|
|||
case ty.Equals(cty.Number):
|
||||
return cty.NumberVal(v.AsBigFloat()), nil
|
||||
default:
|
||||
return cty.NilVal, fmt.Errorf("lookup() can only be used with flat lists")
|
||||
return cty.NilVal, errors.New("lookup() can only be used with flat lists")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -667,7 +705,7 @@ var LookupFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// MapFunc contructs a function that takes an even number of arguments and
|
||||
// MapFunc constructs a function that takes an even number of arguments and
|
||||
// returns a map whose elements are constructed from consecutive pairs of arguments.
|
||||
//
|
||||
// This function is deprecated in Terraform v0.12
|
||||
|
@ -695,7 +733,7 @@ var MapFunc = function.New(&function.Spec{
|
|||
|
||||
valType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if valType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("all arguments must have the same type")
|
||||
return cty.NilType, errors.New("all arguments must have the same type")
|
||||
}
|
||||
|
||||
return cty.Map(valType), nil
|
||||
|
@ -740,7 +778,7 @@ var MapFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// MatchkeysFunc contructs a function that constructs a new list by taking a
|
||||
// MatchkeysFunc constructs a function that constructs a new list by taking a
|
||||
// subset of elements from one list whose indexes match the corresponding
|
||||
// indexes of values in another list.
|
||||
var MatchkeysFunc = function.New(&function.Spec{
|
||||
|
@ -760,7 +798,7 @@ var MatchkeysFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
if !args[1].Type().Equals(args[2].Type()) {
|
||||
return cty.NilType, fmt.Errorf("lists must be of the same type")
|
||||
return cty.NilType, errors.New("lists must be of the same type")
|
||||
}
|
||||
|
||||
return args[0].Type(), nil
|
||||
|
@ -771,7 +809,7 @@ var MatchkeysFunc = function.New(&function.Spec{
|
|||
}
|
||||
|
||||
if args[0].LengthInt() != args[1].LengthInt() {
|
||||
return cty.ListValEmpty(retType.ElementType()), fmt.Errorf("length of keys and values should be equal")
|
||||
return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal")
|
||||
}
|
||||
|
||||
output := make([]cty.Value, 0)
|
||||
|
@ -818,7 +856,7 @@ var MatchkeysFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// MergeFunc contructs a function that takes an arbitrary number of maps and
|
||||
// MergeFunc constructs a function that takes an arbitrary number of maps and
|
||||
// returns a single map that contains a merged set of elements from all of the maps.
|
||||
//
|
||||
// If more than one given map defines the same key then the one that is later in
|
||||
|
@ -906,7 +944,7 @@ var SetProductFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: func(args []cty.Value) (retType cty.Type, err error) {
|
||||
if len(args) < 2 {
|
||||
return cty.NilType, fmt.Errorf("at least two arguments are required")
|
||||
return cty.NilType, errors.New("at least two arguments are required")
|
||||
}
|
||||
|
||||
listCount := 0
|
||||
|
@ -1017,69 +1055,124 @@ var SetProductFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// SliceFunc contructs a function that extracts some consecutive elements
|
||||
// SliceFunc constructs a function that extracts some consecutive elements
|
||||
// from within a list.
|
||||
var SliceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "list",
|
||||
Type: cty.List(cty.DynamicPseudoType),
|
||||
Type: cty.DynamicPseudoType,
|
||||
},
|
||||
{
|
||||
Name: "startIndex",
|
||||
Name: "start_index",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "endIndex",
|
||||
Name: "end_index",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
return args[0].Type(), nil
|
||||
arg := args[0]
|
||||
argTy := arg.Type()
|
||||
|
||||
if argTy.IsSetType() {
|
||||
return cty.NilType, function.NewArgErrorf(0, "cannot slice a set, because its elements do not have indices; use the tolist function to force conversion to list if the ordering of the result is not important")
|
||||
}
|
||||
if !argTy.IsListType() && !argTy.IsTupleType() {
|
||||
return cty.NilType, function.NewArgErrorf(0, "must be a list or tuple value")
|
||||
}
|
||||
|
||||
startIndex, endIndex, idxsKnown, err := sliceIndexes(args)
|
||||
if err != nil {
|
||||
return cty.NilType, err
|
||||
}
|
||||
|
||||
if argTy.IsListType() {
|
||||
return argTy, nil
|
||||
}
|
||||
|
||||
if !idxsKnown {
|
||||
// If we don't know our start/end indices then we can't predict
|
||||
// the result type if we're planning to return a tuple.
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
return cty.Tuple(argTy.TupleElementTypes()[startIndex:endIndex]), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
inputList := args[0]
|
||||
if !inputList.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
var startIndex, endIndex int
|
||||
|
||||
if err = gocty.FromCtyValue(args[1], &startIndex); err != nil {
|
||||
return cty.NilVal, fmt.Errorf("invalid start index: %s", err)
|
||||
}
|
||||
if err = gocty.FromCtyValue(args[2], &endIndex); err != nil {
|
||||
return cty.NilVal, fmt.Errorf("invalid start index: %s", err)
|
||||
if retType == cty.DynamicPseudoType {
|
||||
return cty.DynamicVal, nil
|
||||
}
|
||||
|
||||
if startIndex < 0 {
|
||||
return cty.NilVal, fmt.Errorf("from index must be >= 0")
|
||||
}
|
||||
if endIndex > inputList.LengthInt() {
|
||||
return cty.NilVal, fmt.Errorf("to index must be <= length of the input list")
|
||||
}
|
||||
if startIndex > endIndex {
|
||||
return cty.NilVal, fmt.Errorf("from index must be <= to index")
|
||||
// we ignore idxsKnown return value here because the indices are always
|
||||
// known here, or else the call would've short-circuited.
|
||||
startIndex, endIndex, _, err := sliceIndexes(args)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
var outputList []cty.Value
|
||||
|
||||
i := 0
|
||||
for it := inputList.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
if i >= startIndex && i < endIndex {
|
||||
outputList = append(outputList, v)
|
||||
if endIndex-startIndex == 0 {
|
||||
if retType.IsTupleType() {
|
||||
return cty.EmptyTupleVal, nil
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if len(outputList) == 0 {
|
||||
return cty.ListValEmpty(retType.ElementType()), nil
|
||||
}
|
||||
|
||||
outputList := inputList.AsValueSlice()[startIndex:endIndex]
|
||||
|
||||
if retType.IsTupleType() {
|
||||
return cty.TupleVal(outputList), nil
|
||||
}
|
||||
|
||||
return cty.ListVal(outputList), nil
|
||||
},
|
||||
})
|
||||
|
||||
func sliceIndexes(args []cty.Value) (int, int, bool, error) {
|
||||
var startIndex, endIndex, length int
|
||||
var startKnown, endKnown, lengthKnown bool
|
||||
|
||||
if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known
|
||||
length = args[0].LengthInt()
|
||||
lengthKnown = true
|
||||
}
|
||||
|
||||
if args[1].IsKnown() {
|
||||
if err := gocty.FromCtyValue(args[1], &startIndex); err != nil {
|
||||
return 0, 0, false, function.NewArgErrorf(1, "invalid start index: %s", err)
|
||||
}
|
||||
if startIndex < 0 {
|
||||
return 0, 0, false, function.NewArgErrorf(1, "start index must not be less than zero")
|
||||
}
|
||||
if lengthKnown && startIndex > length {
|
||||
return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than the length of the list")
|
||||
}
|
||||
startKnown = true
|
||||
}
|
||||
if args[2].IsKnown() {
|
||||
if err := gocty.FromCtyValue(args[2], &endIndex); err != nil {
|
||||
return 0, 0, false, function.NewArgErrorf(2, "invalid end index: %s", err)
|
||||
}
|
||||
if endIndex < 0 {
|
||||
return 0, 0, false, function.NewArgErrorf(2, "end index must not be less than zero")
|
||||
}
|
||||
if lengthKnown && endIndex > length {
|
||||
return 0, 0, false, function.NewArgErrorf(2, "end index must not be greater than the length of the list")
|
||||
}
|
||||
endKnown = true
|
||||
}
|
||||
if startKnown && endKnown {
|
||||
if startIndex > endIndex {
|
||||
return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than end index")
|
||||
}
|
||||
}
|
||||
return startIndex, endIndex, startKnown && endKnown, nil
|
||||
}
|
||||
|
||||
// TransposeFunc contructs a function that takes a map of lists of strings and
|
||||
// TransposeFunc constructs a function that takes a map of lists of strings and
|
||||
// swaps the keys and values to produce a new map of lists of strings.
|
||||
var TransposeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
|
@ -1103,7 +1196,7 @@ var TransposeFunc = function.New(&function.Spec{
|
|||
for iter := inVal.ElementIterator(); iter.Next(); {
|
||||
_, val := iter.Element()
|
||||
if !val.Type().Equals(cty.String) {
|
||||
return cty.MapValEmpty(cty.List(cty.String)), fmt.Errorf("input must be a map of lists of strings")
|
||||
return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings")
|
||||
}
|
||||
|
||||
outKey := val.AsString()
|
||||
|
@ -1129,7 +1222,7 @@ var TransposeFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// ValuesFunc contructs a function that returns a list of the map values,
|
||||
// ValuesFunc constructs a function that returns a list of the map values,
|
||||
// in the order of the sorted keys.
|
||||
var ValuesFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
|
@ -1163,7 +1256,7 @@ var ValuesFunc = function.New(&function.Spec{
|
|||
}
|
||||
return cty.Tuple(tys), nil
|
||||
}
|
||||
return cty.NilType, fmt.Errorf("values() requires a map as the first argument")
|
||||
return cty.NilType, errors.New("values() requires a map as the first argument")
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
mapVar := args[0]
|
||||
|
@ -1186,7 +1279,7 @@ var ValuesFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// ZipmapFunc contructs a function that constructs a map from a list of keys
|
||||
// ZipmapFunc constructs a function that constructs a map from a list of keys
|
||||
// and a corresponding list of values.
|
||||
var ZipmapFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
|
@ -1230,7 +1323,7 @@ var ZipmapFunc = function.New(&function.Spec{
|
|||
return cty.Object(atys), nil
|
||||
|
||||
default:
|
||||
return cty.NilType, fmt.Errorf("values argument must be a list or tuple value")
|
||||
return cty.NilType, errors.New("values argument must be a list or tuple value")
|
||||
}
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
|
|
|
@ -88,7 +88,6 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
"replace": funcs.ReplaceFunc,
|
||||
"reverse": funcs.ReverseFunc,
|
||||
"rsadecrypt": funcs.RsaDecryptFunc,
|
||||
"sethaselement": stdlib.SetHasElementFunc,
|
||||
"setintersection": stdlib.SetIntersectionFunc,
|
||||
"setproduct": funcs.SetProductFunc,
|
||||
"setunion": stdlib.SetUnionFunc,
|
||||
|
@ -99,6 +98,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
"slice": funcs.SliceFunc,
|
||||
"sort": funcs.SortFunc,
|
||||
"split": funcs.SplitFunc,
|
||||
"strrev": stdlib.ReverseFunc,
|
||||
"substr": stdlib.SubstrFunc,
|
||||
"timestamp": funcs.TimestampFunc,
|
||||
"timeadd": funcs.TimeAddFunc,
|
||||
|
|
|
@ -4,6 +4,19 @@ package plans
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[NoOp-0]
|
||||
_ = x[Create-43]
|
||||
_ = x[Read-8592]
|
||||
_ = x[Update-126]
|
||||
_ = x[DeleteThenCreate-8723]
|
||||
_ = x[CreateThenDelete-177]
|
||||
_ = x[Delete-45]
|
||||
}
|
||||
|
||||
const (
|
||||
_Action_name_0 = "NoOp"
|
||||
_Action_name_1 = "Create"
|
||||
|
|
|
@ -61,30 +61,22 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu
|
|||
plannedV := planned.GetAttr(name)
|
||||
actualV := actual.GetAttr(name)
|
||||
|
||||
// As a special case, we permit a "planned" block with exactly one
|
||||
// element where all of the "leaf" values are unknown, since that's
|
||||
// what HCL's dynamic block extension generates if the for_each
|
||||
// expression is itself unknown and thus it cannot predict how many
|
||||
// child blocks will get created.
|
||||
switch blockS.Nesting {
|
||||
case configschema.NestingSingle, configschema.NestingGroup:
|
||||
if allLeafValuesUnknown(plannedV) && !plannedV.IsNull() {
|
||||
return errs
|
||||
}
|
||||
case configschema.NestingList, configschema.NestingMap, configschema.NestingSet:
|
||||
if plannedV.IsKnown() && !plannedV.IsNull() && plannedV.LengthInt() == 1 {
|
||||
elemVs := plannedV.AsValueSlice()
|
||||
if allLeafValuesUnknown(elemVs[0]) {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
|
||||
}
|
||||
// As a special case, if there were any blocks whose leaf attributes
|
||||
// are all unknown then we assume (possibly incorrectly) that the
|
||||
// HCL dynamic block extension is in use with an unknown for_each
|
||||
// argument, and so we will do looser validation here that allows
|
||||
// for those blocks to have expanded into a different number of blocks
|
||||
// if the for_each value is now known.
|
||||
maybeUnknownBlocks := couldHaveUnknownBlockPlaceholder(plannedV, blockS, false)
|
||||
|
||||
path := append(path, cty.GetAttrStep{Name: name})
|
||||
switch blockS.Nesting {
|
||||
case configschema.NestingSingle, configschema.NestingGroup:
|
||||
// If an unknown block placeholder was present then the placeholder
|
||||
// may have expanded out into zero blocks, which is okay.
|
||||
if maybeUnknownBlocks && actualV.IsNull() {
|
||||
continue
|
||||
}
|
||||
moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path)
|
||||
errs = append(errs, moreErrs...)
|
||||
case configschema.NestingList:
|
||||
|
@ -96,6 +88,14 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu
|
|||
continue
|
||||
}
|
||||
|
||||
if maybeUnknownBlocks {
|
||||
// When unknown blocks are present the final blocks may be
|
||||
// at different indices than the planned blocks, so unfortunately
|
||||
// we can't do our usual checks in this case without generating
|
||||
// false negatives.
|
||||
continue
|
||||
}
|
||||
|
||||
plannedL := plannedV.LengthInt()
|
||||
actualL := actualV.LengthInt()
|
||||
if plannedL != actualL {
|
||||
|
@ -130,10 +130,12 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu
|
|||
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.GetAttrStep{Name: k}))
|
||||
errs = append(errs, moreErrs...)
|
||||
}
|
||||
for k := range actualAtys {
|
||||
if _, ok := plannedAtys[k]; !ok {
|
||||
errs = append(errs, path.NewErrorf("new block key %q has appeared", k))
|
||||
continue
|
||||
if !maybeUnknownBlocks { // new blocks may appear if unknown blocks were present in the plan
|
||||
for k := range actualAtys {
|
||||
if _, ok := plannedAtys[k]; !ok {
|
||||
errs = append(errs, path.NewErrorf("new block key %q has appeared", k))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -142,7 +144,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu
|
|||
}
|
||||
plannedL := plannedV.LengthInt()
|
||||
actualL := actualV.LengthInt()
|
||||
if plannedL != actualL {
|
||||
if plannedL != actualL && !maybeUnknownBlocks { // new blocks may appear if unknown blocks were persent in the plan
|
||||
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
|
||||
continue
|
||||
}
|
||||
|
@ -302,18 +304,77 @@ func indexStrForErrors(v cty.Value) string {
|
|||
}
|
||||
}
|
||||
|
||||
func allLeafValuesUnknown(v cty.Value) bool {
|
||||
seenKnownValue := false
|
||||
cty.Walk(v, func(path cty.Path, cv cty.Value) (bool, error) {
|
||||
if cv.IsNull() {
|
||||
seenKnownValue = true
|
||||
// couldHaveUnknownBlockPlaceholder is a heuristic that recognizes how the
|
||||
// HCL dynamic block extension behaves when it's asked to expand a block whose
|
||||
// for_each argument is unknown. In such cases, it generates a single placeholder
|
||||
// block with all leaf attribute values unknown, and once the for_each
|
||||
// expression becomes known the placeholder may be replaced with any number
|
||||
// of blocks, so object compatibility checks would need to be more liberal.
|
||||
//
|
||||
// Set "nested" if testing a block that is nested inside a candidate block
|
||||
// placeholder; this changes the interpretation of there being no blocks of
|
||||
// a type to allow for there being zero nested blocks.
|
||||
func couldHaveUnknownBlockPlaceholder(v cty.Value, blockS *configschema.NestedBlock, nested bool) bool {
|
||||
switch blockS.Nesting {
|
||||
case configschema.NestingSingle, configschema.NestingGroup:
|
||||
if nested && v.IsNull() {
|
||||
return true // for nested blocks, a single block being unset doesn't disqualify from being an unknown block placeholder
|
||||
}
|
||||
if cv.Type().IsPrimitiveType() && cv.IsKnown() {
|
||||
seenKnownValue = true
|
||||
return couldBeUnknownBlockPlaceholderElement(v, &blockS.Block)
|
||||
default:
|
||||
// These situations should be impossible for correct providers, but
|
||||
// we permit the legacy SDK to produce some incorrect outcomes
|
||||
// for compatibility with its existing logic, and so we must be
|
||||
// tolerant here.
|
||||
if !v.IsKnown() {
|
||||
return true
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
return !seenKnownValue
|
||||
if v.IsNull() {
|
||||
return false // treated as if the list were empty, so we would see zero iterations below
|
||||
}
|
||||
|
||||
// For all other nesting modes, our value should be something iterable.
|
||||
for it := v.ElementIterator(); it.Next(); {
|
||||
_, ev := it.Element()
|
||||
if couldBeUnknownBlockPlaceholderElement(ev, &blockS.Block) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Our default changes depending on whether we're testing the candidate
|
||||
// block itself or something nested inside of it: zero blocks of a type
|
||||
// can never contain a dynamic block placeholder, but a dynamic block
|
||||
// placeholder might contain zero blocks of one of its own nested block
|
||||
// types, if none were set in the config at all.
|
||||
return nested
|
||||
}
|
||||
}
|
||||
|
||||
func couldBeUnknownBlockPlaceholderElement(v cty.Value, schema *configschema.Block) bool {
|
||||
if v.IsNull() {
|
||||
return false // null value can never be a placeholder element
|
||||
}
|
||||
if !v.IsKnown() {
|
||||
return true // this should never happen for well-behaved providers, but can happen with the legacy SDK opt-outs
|
||||
}
|
||||
for name := range schema.Attributes {
|
||||
av := v.GetAttr(name)
|
||||
|
||||
// Unknown block placeholders contain only unknown or null attribute
|
||||
// values, depending on whether or not a particular attribute was set
|
||||
// explicitly inside the content block. Note that this is imprecise:
|
||||
// non-placeholders can also match this, so this function can generate
|
||||
// false positives.
|
||||
if av.IsKnown() && !av.IsNull() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for name, blockS := range schema.BlockTypes {
|
||||
if !couldHaveUnknownBlockPlaceholder(v.GetAttr(name), blockS, true) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// assertSetValuesCompatible checks that each of the elements in a can
|
||||
|
|
|
@ -228,9 +228,9 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, t
|
|||
}
|
||||
}
|
||||
|
||||
printedProviderName := fmt.Sprintf("%s (%s)", provider, providerSource)
|
||||
i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %q (%s)...", printedProviderName, versionMeta.Version))
|
||||
log.Printf("[DEBUG] getting provider %q version %q", printedProviderName, versionMeta.Version)
|
||||
printedProviderName := fmt.Sprintf("%q (%s)", provider, providerSource)
|
||||
i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %s %s...", printedProviderName, versionMeta.Version))
|
||||
log.Printf("[DEBUG] getting provider %s version %q", printedProviderName, versionMeta.Version)
|
||||
err = i.install(provider, v, providerURL)
|
||||
if err != nil {
|
||||
return PluginMeta{}, diags, err
|
||||
|
@ -298,7 +298,7 @@ func (i *ProviderInstaller) install(provider string, version Version, url string
|
|||
// check if the target dir exists, and create it if not
|
||||
var err error
|
||||
if _, StatErr := os.Stat(i.Dir); os.IsNotExist(StatErr) {
|
||||
err = os.Mkdir(i.Dir, 0700)
|
||||
err = os.MkdirAll(i.Dir, 0700)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -3,7 +3,6 @@ package plugin
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
|
@ -45,9 +44,9 @@ type GRPCProvider struct {
|
|||
// This allows the GRPCProvider a way to shutdown the plugin process.
|
||||
PluginClient *plugin.Client
|
||||
|
||||
// TestListener contains a net.Conn to close when the GRPCProvider is being
|
||||
// TestServer contains a grpc.Server to close when the GRPCProvider is being
|
||||
// used in an end to end test of a provider.
|
||||
TestListener io.Closer
|
||||
TestServer *grpc.Server
|
||||
|
||||
// Proto client use to make the grpc service calls.
|
||||
client proto.ProviderClient
|
||||
|
@ -544,9 +543,9 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
|
|||
func (p *GRPCProvider) Close() error {
|
||||
log.Printf("[TRACE] GRPCProvider: Close")
|
||||
|
||||
// close the remote listener if we're running within a test
|
||||
if p.TestListener != nil {
|
||||
p.TestListener.Close()
|
||||
// Make sure to stop the server if we're not running within go-plugin.
|
||||
if p.TestServer != nil {
|
||||
p.TestServer.Stop()
|
||||
}
|
||||
|
||||
// Check this since it's not automatically inserted during plugin creation.
|
||||
|
|
|
@ -4,6 +4,15 @@ package states
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[NoEach-0]
|
||||
_ = x[EachList-76]
|
||||
_ = x[EachMap-77]
|
||||
}
|
||||
|
||||
const (
|
||||
_EachMode_name_0 = "NoEach"
|
||||
_EachMode_name_1 = "EachListEachMap"
|
||||
|
|
|
@ -4,6 +4,15 @@ package states
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ObjectReady-82]
|
||||
_ = x[ObjectTainted-84]
|
||||
_ = x[ObjectPlanned-80]
|
||||
}
|
||||
|
||||
const (
|
||||
_ObjectStatus_name_0 = "ObjectPlanned"
|
||||
_ObjectStatus_name_1 = "ObjectReady"
|
||||
|
|
|
@ -301,7 +301,7 @@ func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2,
|
|||
|
||||
dependencies := make([]string, len(rsOld.Dependencies))
|
||||
for i, v := range rsOld.Dependencies {
|
||||
dependencies[i] = strings.TrimSuffix(v, ".*")
|
||||
dependencies[i] = parseLegacyDependency(v)
|
||||
}
|
||||
|
||||
return &instanceObjectStateV4{
|
||||
|
@ -413,3 +413,19 @@ func simplifyImpliedValueType(ty cty.Type) cty.Type {
|
|||
return ty
|
||||
}
|
||||
}
|
||||
|
||||
func parseLegacyDependency(s string) string {
|
||||
parts := strings.Split(s, ".")
|
||||
ret := parts[0]
|
||||
for _, part := range parts[1:] {
|
||||
if part == "*" {
|
||||
break
|
||||
}
|
||||
if i, err := strconv.Atoi(part); err == nil {
|
||||
ret = ret + fmt.Sprintf("[%d]", i)
|
||||
break
|
||||
}
|
||||
ret = ret + "." + part
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -23,36 +23,10 @@ import (
|
|||
// the "count" behavior should not be enabled for this resource at all.
|
||||
//
|
||||
// If error diagnostics are returned then the result is always the meaningless
|
||||
// placeholder value -1, except in one case: if the count expression evaluates
|
||||
// to an unknown number value then the result is zero, allowing this situation
|
||||
// to be treated by the caller as special if needed. For example, an early
|
||||
// graph walk may wish to just silently skip resources with unknown counts
|
||||
// to allow them to be dealt with in a later graph walk where more information
|
||||
// is available.
|
||||
// placeholder value -1.
|
||||
func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) {
|
||||
if expr == nil {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
var count int
|
||||
|
||||
countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil)
|
||||
diags = diags.Append(countDiags)
|
||||
if diags.HasErrors() {
|
||||
return -1, diags
|
||||
}
|
||||
|
||||
switch {
|
||||
case countVal.IsNull():
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid count argument",
|
||||
Detail: `The given "count" argument value is null. An integer is required.`,
|
||||
Subject: expr.Range().Ptr(),
|
||||
})
|
||||
return -1, diags
|
||||
case !countVal.IsKnown():
|
||||
count, known, diags := evaluateResourceCountExpressionKnown(expr, ctx)
|
||||
if !known {
|
||||
// Currently this is a rather bad outcome from a UX standpoint, since we have
|
||||
// no real mechanism to deal with this situation and all we can do is produce
|
||||
// an error message.
|
||||
|
@ -65,11 +39,36 @@ func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int,
|
|||
Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`,
|
||||
Subject: expr.Range().Ptr(),
|
||||
})
|
||||
// We return zero+errors in this one case to allow callers to handle
|
||||
// an unknown count as special. This is rarely necessary, but is used
|
||||
// by the validate walk in particular so that it can just skip
|
||||
// validation in this case, assuming a later walk will take care of it.
|
||||
return 0, diags
|
||||
}
|
||||
return count, diags
|
||||
}
|
||||
|
||||
// evaluateResourceCountExpressionKnown is like evaluateResourceCountExpression
|
||||
// except that it handles an unknown result by returning count = 0 and
|
||||
// a known = false, rather than by reporting the unknown value as an error
|
||||
// diagnostic.
|
||||
func evaluateResourceCountExpressionKnown(expr hcl.Expression, ctx EvalContext) (count int, known bool, diags tfdiags.Diagnostics) {
|
||||
if expr == nil {
|
||||
return -1, true, nil
|
||||
}
|
||||
|
||||
countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil)
|
||||
diags = diags.Append(countDiags)
|
||||
if diags.HasErrors() {
|
||||
return -1, true, diags
|
||||
}
|
||||
|
||||
switch {
|
||||
case countVal.IsNull():
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid count argument",
|
||||
Detail: `The given "count" argument value is null. An integer is required.`,
|
||||
Subject: expr.Range().Ptr(),
|
||||
})
|
||||
return -1, true, diags
|
||||
case !countVal.IsKnown():
|
||||
return 0, false, diags
|
||||
}
|
||||
|
||||
err := gocty.FromCtyValue(countVal, &count)
|
||||
|
@ -80,7 +79,7 @@ func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int,
|
|||
Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err),
|
||||
Subject: expr.Range().Ptr(),
|
||||
})
|
||||
return -1, diags
|
||||
return -1, true, diags
|
||||
}
|
||||
if count < 0 {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
|
@ -89,10 +88,10 @@ func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int,
|
|||
Detail: `The given "count" argument value is unsuitable: negative numbers are not supported.`,
|
||||
Subject: expr.Range().Ptr(),
|
||||
})
|
||||
return -1, diags
|
||||
return -1, true, diags
|
||||
}
|
||||
|
||||
return count, diags
|
||||
return count, true, diags
|
||||
}
|
||||
|
||||
// fixResourceCountSetTransition is a helper function to fix up the state when a
|
||||
|
|
|
@ -368,7 +368,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
|
|||
}
|
||||
plannedNewVal = resp.PlannedState
|
||||
plannedPrivate = resp.PlannedPrivate
|
||||
for _, err := range schema.ImpliedType().TestConformance(plannedNewVal.Type()) {
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid plan",
|
||||
|
|
|
@ -291,125 +291,6 @@ func (n *EvalReadData) Eval(ctx EvalContext) (interface{}, error) {
|
|||
return nil, diags.ErrWithWarnings()
|
||||
}
|
||||
|
||||
// EvalReadDataDiff is an EvalNode implementation that executes a data
|
||||
// resource's ReadDataDiff method to discover what attributes it exports.
|
||||
type EvalReadDataDiff struct {
|
||||
Addr addrs.ResourceInstance
|
||||
Config *configs.Resource
|
||||
ProviderAddr addrs.AbsProviderConfig
|
||||
ProviderSchema **ProviderSchema
|
||||
|
||||
Output **plans.ResourceInstanceChange
|
||||
OutputValue *cty.Value
|
||||
OutputConfigValue *cty.Value
|
||||
OutputState **states.ResourceInstanceObject
|
||||
|
||||
// Set Previous when re-evaluating diff during apply, to ensure that
|
||||
// the "Destroy" flag is preserved.
|
||||
Previous **plans.ResourceInstanceChange
|
||||
}
|
||||
|
||||
func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||
absAddr := n.Addr.Absolute(ctx.Path())
|
||||
|
||||
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
|
||||
return nil, fmt.Errorf("provider schema not available for %s", n.Addr)
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
var change *plans.ResourceInstanceChange
|
||||
var configVal cty.Value
|
||||
|
||||
if n.Previous != nil && *n.Previous != nil && (*n.Previous).Action == plans.Delete {
|
||||
// If we're re-diffing for a diff that was already planning to
|
||||
// destroy, then we'll just continue with that plan.
|
||||
|
||||
nullVal := cty.NullVal(cty.DynamicPseudoType)
|
||||
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
return h.PreDiff(absAddr, states.CurrentGen, nullVal, nullVal)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
change = &plans.ResourceInstanceChange{
|
||||
Addr: absAddr,
|
||||
ProviderAddr: n.ProviderAddr,
|
||||
Change: plans.Change{
|
||||
Action: plans.Delete,
|
||||
Before: nullVal,
|
||||
After: nullVal,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
config := *n.Config
|
||||
providerSchema := *n.ProviderSchema
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
|
||||
if schema == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
return nil, fmt.Errorf("provider does not support data source %q", n.Addr.Resource.Type)
|
||||
}
|
||||
|
||||
objTy := schema.ImpliedType()
|
||||
priorVal := cty.NullVal(objTy) // for data resources, prior is always null because we start fresh every time
|
||||
|
||||
keyData := EvalDataForInstanceKey(n.Addr.Key)
|
||||
|
||||
var configDiags tfdiags.Diagnostics
|
||||
configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return nil, diags.Err()
|
||||
}
|
||||
|
||||
proposedNewVal := objchange.ProposedNewObject(schema, priorVal, configVal)
|
||||
|
||||
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
change = &plans.ResourceInstanceChange{
|
||||
Addr: absAddr,
|
||||
ProviderAddr: n.ProviderAddr,
|
||||
Change: plans.Change{
|
||||
Action: plans.Read,
|
||||
Before: priorVal,
|
||||
After: proposedNewVal,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
return h.PostDiff(absAddr, states.CurrentGen, change.Action, change.Before, change.After)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n.Output != nil {
|
||||
*n.Output = change
|
||||
}
|
||||
if n.OutputValue != nil {
|
||||
*n.OutputValue = change.After
|
||||
}
|
||||
if n.OutputConfigValue != nil {
|
||||
*n.OutputConfigValue = configVal
|
||||
}
|
||||
|
||||
if n.OutputState != nil {
|
||||
state := &states.ResourceInstanceObject{
|
||||
Value: change.After,
|
||||
Status: states.ObjectReady,
|
||||
}
|
||||
*n.OutputState = state
|
||||
}
|
||||
|
||||
return nil, diags.ErrWithWarnings()
|
||||
}
|
||||
|
||||
// EvalReadDataApply is an EvalNode implementation that executes a data
|
||||
// resource's ReadDataApply method to read data from the data source.
|
||||
type EvalReadDataApply struct {
|
||||
|
|
|
@ -19,26 +19,13 @@ import (
|
|||
// If any errors occur during upgrade, error diagnostics are returned. In that
|
||||
// case it is not safe to proceed with using the original state object.
|
||||
func UpgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema *configschema.Block, currentVersion uint64) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) {
|
||||
currentTy := currentSchema.ImpliedType()
|
||||
|
||||
// If the state is currently in flatmap format and the current schema
|
||||
// contains DynamicPseudoType attributes then we won't be able to convert
|
||||
// it to JSON without the provider's help even if the schema version matches,
|
||||
// since only the provider knows how to interpret the dynamic attribute
|
||||
// value in flatmap format to convert it to JSON.
|
||||
schemaHasDynamic := currentTy.HasDynamicTypes()
|
||||
stateIsFlatmap := len(src.AttrsJSON) == 0
|
||||
forceProviderUpgrade := schemaHasDynamic && stateIsFlatmap
|
||||
|
||||
if src.SchemaVersion == currentVersion && !forceProviderUpgrade {
|
||||
// No upgrading required, then.
|
||||
return src, nil
|
||||
}
|
||||
if addr.Resource.Resource.Mode != addrs.ManagedResourceMode {
|
||||
// We only do state upgrading for managed resources.
|
||||
return src, nil
|
||||
}
|
||||
|
||||
stateIsFlatmap := len(src.AttrsJSON) == 0
|
||||
|
||||
providerType := addr.Resource.Resource.DefaultProviderConfig().Type
|
||||
if src.SchemaVersion > currentVersion {
|
||||
log.Printf("[TRACE] UpgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentVersion)
|
||||
|
@ -60,7 +47,11 @@ func UpgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
// v0.12, this also includes translating from legacy flatmap to new-style
|
||||
// representation, since only the provider has enough information to
|
||||
// understand a flatmap built against an older schema.
|
||||
log.Printf("[TRACE] UpgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType)
|
||||
if src.SchemaVersion != currentVersion {
|
||||
log.Printf("[TRACE] UpgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType)
|
||||
} else {
|
||||
log.Printf("[TRACE] UpgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentVersion, providerType)
|
||||
}
|
||||
|
||||
req := providers.UpgradeResourceStateRequest{
|
||||
TypeName: addr.Resource.Resource.Type,
|
||||
|
|
|
@ -4,6 +4,20 @@ package terraform
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[GraphTypeInvalid-0]
|
||||
_ = x[GraphTypeLegacy-1]
|
||||
_ = x[GraphTypeRefresh-2]
|
||||
_ = x[GraphTypePlan-3]
|
||||
_ = x[GraphTypePlanDestroy-4]
|
||||
_ = x[GraphTypeApply-5]
|
||||
_ = x[GraphTypeValidate-6]
|
||||
_ = x[GraphTypeEval-7]
|
||||
}
|
||||
|
||||
const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeValidateGraphTypeEval"
|
||||
|
||||
var _GraphType_index = [...]uint8{0, 16, 31, 47, 60, 80, 94, 111, 124}
|
||||
|
|
|
@ -4,6 +4,16 @@ package terraform
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[TypeInvalid-0]
|
||||
_ = x[TypePrimary-1]
|
||||
_ = x[TypeTainted-2]
|
||||
_ = x[TypeDeposed-3]
|
||||
}
|
||||
|
||||
const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed"
|
||||
|
||||
var _InstanceType_index = [...]uint8{0, 11, 22, 33, 44}
|
||||
|
|
|
@ -27,11 +27,16 @@ var (
|
|||
func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
|
||||
count, countKnown, countDiags := evaluateResourceCountExpressionKnown(n.Config.Count, ctx)
|
||||
diags = diags.Append(countDiags)
|
||||
if countDiags.HasErrors() {
|
||||
return nil, diags.Err()
|
||||
}
|
||||
if !countKnown {
|
||||
// If the count isn't known yet, we'll skip refreshing and try expansion
|
||||
// again during the plan walk.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Next we need to potentially rename an instance address in the state
|
||||
// if we're transitioning whether "count" is set at all.
|
||||
|
|
22
vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_instance.go
generated
vendored
22
vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_instance.go
generated
vendored
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// NodeApplyableResourceInstance represents a resource instance that is
|
||||
|
@ -107,6 +108,27 @@ func (n *NodeApplyableResourceInstance) EvalTree() EvalNode {
|
|||
// Determine the dependencies for the state.
|
||||
stateDeps := n.StateReferences()
|
||||
|
||||
if n.Config == nil {
|
||||
// This should not be possible, but we've got here in at least one
|
||||
// case as discussed in the following issue:
|
||||
// https://github.com/hashicorp/terraform/issues/21258
|
||||
// To avoid an outright crash here, we'll instead return an explicit
|
||||
// error.
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Resource node has no configuration attached",
|
||||
fmt.Sprintf(
|
||||
"The graph node for %s has no configuration attached to it. This suggests a bug in Terraform's apply graph builder; please report it!",
|
||||
addr,
|
||||
),
|
||||
))
|
||||
err := diags.Err()
|
||||
return &EvalReturnError{
|
||||
Error: &err,
|
||||
}
|
||||
}
|
||||
|
||||
// Eval info is different depending on what kind of resource this is
|
||||
switch n.Config.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
|
|
27
vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go
generated
vendored
27
vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go
generated
vendored
|
@ -81,8 +81,31 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
|
|||
// here.
|
||||
&EvalIf{
|
||||
If: func(ctx EvalContext) (bool, error) {
|
||||
if state != nil && state.Status != states.ObjectPlanned {
|
||||
return true, EvalEarlyExitError{}
|
||||
depChanges := false
|
||||
|
||||
// Check and see if any of our dependencies have changes.
|
||||
changes := ctx.Changes()
|
||||
for _, d := range n.StateReferences() {
|
||||
ri, ok := d.(addrs.ResourceInstance)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
change := changes.GetResourceInstanceChange(ri.Absolute(ctx.Path()), states.CurrentGen)
|
||||
if change != nil && change.Action != plans.NoOp {
|
||||
depChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
refreshed := state != nil && state.Status != states.ObjectPlanned
|
||||
|
||||
// If there are no dependency changes, and it's not a forced
|
||||
// read because we there was no Refresh, then we don't need
|
||||
// to re-read. If any dependencies have changes, it means
|
||||
// our config may also have changes and we need to Read the
|
||||
// data source again.
|
||||
if !depChanges && refreshed {
|
||||
return false, EvalEarlyExitError{}
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
|
@ -191,6 +192,10 @@ func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ
|
|||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
schemas := p.getSchema()
|
||||
schema := schemas.ResourceTypes[r.TypeName]
|
||||
schemaType := schema.Block.ImpliedType()
|
||||
|
||||
p.UpgradeResourceStateCalled = true
|
||||
p.UpgradeResourceStateRequest = r
|
||||
|
||||
|
@ -198,7 +203,28 @@ func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ
|
|||
return p.UpgradeResourceStateFn(r)
|
||||
}
|
||||
|
||||
return p.UpgradeResourceStateResponse
|
||||
resp := p.UpgradeResourceStateResponse
|
||||
|
||||
if resp.UpgradedState == cty.NilVal {
|
||||
switch {
|
||||
case r.RawStateFlatmap != nil:
|
||||
v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.UpgradedState = v
|
||||
case len(r.RawStateJSON) > 0:
|
||||
v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.UpgradedState = v
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse {
|
||||
|
|
|
@ -2,7 +2,6 @@ package terraform
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -236,7 +235,7 @@ func NewResourceConfigShimmed(val cty.Value, schema *configschema.Block) *Resour
|
|||
// schema here so that we can preserve the expected invariant
|
||||
// that an attribute is always either wholly known or wholly unknown, while
|
||||
// a child block can be partially unknown.
|
||||
ret.ComputedKeys = newResourceConfigShimmedComputedKeys(val, schema, "")
|
||||
ret.ComputedKeys = newResourceConfigShimmedComputedKeys(val, "")
|
||||
} else {
|
||||
ret.Config = make(map[string]interface{})
|
||||
}
|
||||
|
@ -245,72 +244,45 @@ func NewResourceConfigShimmed(val cty.Value, schema *configschema.Block) *Resour
|
|||
return ret
|
||||
}
|
||||
|
||||
// newResourceConfigShimmedComputedKeys finds all of the unknown values in the
|
||||
// given object, which must conform to the given schema, returning them in
|
||||
// the format that's expected for ResourceConfig.ComputedKeys.
|
||||
func newResourceConfigShimmedComputedKeys(obj cty.Value, schema *configschema.Block, prefix string) []string {
|
||||
// Record the any config values in ComputedKeys. This field had been unused in
|
||||
// helper/schema, but in the new protocol we're using this so that the SDK can
|
||||
// now handle having an unknown collection. The legacy diff code doesn't
|
||||
// properly handle the unknown, because it can't be expressed in the same way
|
||||
// between the config and diff.
|
||||
func newResourceConfigShimmedComputedKeys(val cty.Value, path string) []string {
|
||||
var ret []string
|
||||
ty := obj.Type()
|
||||
ty := val.Type()
|
||||
|
||||
if schema == nil {
|
||||
log.Printf("[WARN] NewResourceConfigShimmed: can't identify computed keys because no schema is available")
|
||||
return nil
|
||||
if val.IsNull() {
|
||||
return ret
|
||||
}
|
||||
|
||||
for attrName := range schema.Attributes {
|
||||
if !ty.HasAttribute(attrName) {
|
||||
// Should never happen, but we'll tolerate it anyway
|
||||
continue
|
||||
}
|
||||
|
||||
attrVal := obj.GetAttr(attrName)
|
||||
if !attrVal.IsWhollyKnown() {
|
||||
ret = append(ret, prefix+attrName)
|
||||
if !val.IsKnown() {
|
||||
// we shouldn't have an entirely unknown resource, but prevent empty
|
||||
// strings just in case
|
||||
if len(path) > 0 {
|
||||
ret = append(ret, path)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
for typeName, blockS := range schema.BlockTypes {
|
||||
if !ty.HasAttribute(typeName) {
|
||||
// Should never happen, but we'll tolerate it anyway
|
||||
continue
|
||||
}
|
||||
|
||||
blockVal := obj.GetAttr(typeName)
|
||||
if blockVal.IsNull() || !blockVal.IsKnown() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch blockS.Nesting {
|
||||
case configschema.NestingSingle, configschema.NestingGroup:
|
||||
keys := newResourceConfigShimmedComputedKeys(blockVal, &blockS.Block, fmt.Sprintf("%s%s.", prefix, typeName))
|
||||
if path != "" {
|
||||
path += "."
|
||||
}
|
||||
switch {
|
||||
case ty.IsListType(), ty.IsTupleType(), ty.IsSetType():
|
||||
i := 0
|
||||
for it := val.ElementIterator(); it.Next(); i++ {
|
||||
_, subVal := it.Element()
|
||||
keys := newResourceConfigShimmedComputedKeys(subVal, fmt.Sprintf("%s%d", path, i))
|
||||
ret = append(ret, keys...)
|
||||
}
|
||||
|
||||
case ty.IsMapType(), ty.IsObjectType():
|
||||
for it := val.ElementIterator(); it.Next(); {
|
||||
subK, subVal := it.Element()
|
||||
keys := newResourceConfigShimmedComputedKeys(subVal, fmt.Sprintf("%s%s", path, subK.AsString()))
|
||||
ret = append(ret, keys...)
|
||||
case configschema.NestingList, configschema.NestingSet:
|
||||
// Producing computed keys items for sets is not really useful
|
||||
// since they are not usefully addressable anyway, but we'll treat
|
||||
// them like lists just so that ret.ComputedKeys accounts for them
|
||||
// all. Our legacy system didn't support sets here anyway, so
|
||||
// treating them as lists is the most accurate translation. Although
|
||||
// set traversal isn't in any particular order, it is _stable_ as
|
||||
// long as the list isn't mutated, and so we know we'll see the
|
||||
// same order here as hcl2shim.ConfigValueFromHCL2 would've seen
|
||||
// inside NewResourceConfigShimmed above.
|
||||
i := 0
|
||||
for it := blockVal.ElementIterator(); it.Next(); i++ {
|
||||
_, subVal := it.Element()
|
||||
subPrefix := fmt.Sprintf("%s.%s%d.", typeName, prefix, i)
|
||||
keys := newResourceConfigShimmedComputedKeys(subVal, &blockS.Block, subPrefix)
|
||||
ret = append(ret, keys...)
|
||||
}
|
||||
case configschema.NestingMap:
|
||||
for it := blockVal.ElementIterator(); it.Next(); {
|
||||
subK, subVal := it.Element()
|
||||
subPrefix := fmt.Sprintf("%s.%s%s.", typeName, prefix, subK.AsString())
|
||||
keys := newResourceConfigShimmedComputedKeys(subVal, &blockS.Block, subPrefix)
|
||||
ret = append(ret, keys...)
|
||||
}
|
||||
default:
|
||||
// Should never happen, since the above is exhaustive.
|
||||
panic(fmt.Errorf("unsupported block nesting type %s", blockS.Nesting))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,21 @@ package terraform
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ValueFromUnknown-0]
|
||||
_ = x[ValueFromConfig-67]
|
||||
_ = x[ValueFromAutoFile-70]
|
||||
_ = x[ValueFromNamedFile-78]
|
||||
_ = x[ValueFromCLIArg-65]
|
||||
_ = x[ValueFromEnvVar-69]
|
||||
_ = x[ValueFromInput-73]
|
||||
_ = x[ValueFromPlan-80]
|
||||
_ = x[ValueFromCaller-83]
|
||||
}
|
||||
|
||||
const (
|
||||
_ValueSourceType_name_0 = "ValueFromUnknown"
|
||||
_ValueSourceType_name_1 = "ValueFromCLIArg"
|
||||
|
|
|
@ -4,6 +4,21 @@ package terraform
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[walkInvalid-0]
|
||||
_ = x[walkApply-1]
|
||||
_ = x[walkPlan-2]
|
||||
_ = x[walkPlanDestroy-3]
|
||||
_ = x[walkRefresh-4]
|
||||
_ = x[walkValidate-5]
|
||||
_ = x[walkDestroy-6]
|
||||
_ = x[walkImport-7]
|
||||
_ = x[walkEval-8]
|
||||
}
|
||||
|
||||
const _walkOperation_name = "walkInvalidwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImportwalkEval"
|
||||
|
||||
var _walkOperation_index = [...]uint8{0, 11, 20, 28, 43, 54, 66, 77, 87, 95}
|
||||
|
|
|
@ -4,6 +4,14 @@ package tfdiags
|
|||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[Error-69]
|
||||
_ = x[Warning-87]
|
||||
}
|
||||
|
||||
const (
|
||||
_Severity_name_0 = "Error"
|
||||
_Severity_name_1 = "Warning"
|
||||
|
|
|
@ -138,7 +138,7 @@ func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) con
|
|||
if len(tupleEtys) == 0 {
|
||||
// Empty tuple short-circuit
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
return cty.ListValEmpty(listEty), nil
|
||||
return cty.SetValEmpty(listEty), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,11 @@ var FormatListFunc = function.New(&function.Spec{
|
|||
argTy := arg.Type()
|
||||
switch {
|
||||
case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
|
||||
if !argTy.IsTupleType() && !arg.IsKnown() {
|
||||
// We can't iterate this one at all yet then, so we can't
|
||||
// yet produce a result.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
thisLen := arg.LengthInt()
|
||||
if iterLen == -1 {
|
||||
iterLen = thisLen
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue