1
1
package terraform
2
2
3
3
import (
4
+ "archive/zip"
4
5
"fmt"
6
+ "io"
7
+ "io/ioutil"
8
+ "net/http"
5
9
"os"
6
10
"os/exec"
11
+ "path/filepath"
12
+ "runtime"
7
13
"strings"
8
14
9
15
"github.com/hashicorp/go-hclog"
@@ -28,6 +34,11 @@ type PluginManager struct {
28
34
func (m * PluginManager ) Provider (provider , version string , forceLocal bool ) (* plugin.Client , discovery.PluginMeta , error ) {
29
35
meta , ok := m .getLocal ("provider" , provider , version )
30
36
if ! ok && ! forceLocal {
37
+ meta , ok , _ = m .getProviderRemoteDirectDownload (provider , version )
38
+ if ok {
39
+ return client (meta ), meta , nil
40
+ }
41
+
31
42
var err error
32
43
meta , _ , err = m .getProviderRemote (provider , version )
33
44
if err != nil {
@@ -87,13 +98,85 @@ func client(m discovery.PluginMeta) *plugin.Client {
87
98
})
88
99
}
89
100
101
+ const releaseTemplateURL = "https://releases.hashicorp.com/terraform-provider-%s/%s/terraform-provider-%[1]s_%[2]s_%s_%s.zip"
102
+
103
+ func (m * PluginManager ) getProviderRemoteDirectDownload (provider , v string ) (discovery.PluginMeta , bool , error ) {
104
+ url := fmt .Sprintf (releaseTemplateURL , provider , v , runtime .GOOS , runtime .GOARCH )
105
+ if err := m .downloadURL (url ); err != nil {
106
+ return discovery.PluginMeta {}, false , err
107
+ }
108
+
109
+ meta , ok := m .getLocal ("provider" , provider , v )
110
+ return meta , ok , nil
111
+ }
112
+
113
+ func (m * PluginManager ) downloadURL (url string ) error {
114
+ resp , err := http .Get (url )
115
+ if err != nil {
116
+ return fmt .Errorf ("error downloading %s file: %w" , url , err )
117
+ }
118
+
119
+ if resp .StatusCode != http .StatusOK {
120
+ return fmt .Errorf ("invalid URL: %s" , url )
121
+ }
122
+
123
+ defer resp .Body .Close ()
124
+ file , err := ioutil .TempFile ("" , "ascode" )
125
+ if err != nil {
126
+ return err
127
+
128
+ }
129
+
130
+ if _ , err := io .Copy (file , resp .Body ); err != nil {
131
+ return fmt .Errorf ("error downloading %s file: %w" , url , err )
132
+ }
133
+
134
+ file .Close ()
135
+ defer os .Remove (file .Name ())
136
+
137
+ archive , err := zip .OpenReader (file .Name ())
138
+ if err != nil {
139
+ panic (err )
140
+ }
141
+
142
+ defer archive .Close ()
143
+
144
+ for _ , f := range archive .File {
145
+ file := filepath .Join (m .Path , f .Name )
146
+
147
+ if ! strings .HasPrefix (file , filepath .Clean (m .Path )+ string (os .PathSeparator )) {
148
+ return fmt .Errorf ("invalid path" )
149
+ }
150
+
151
+ output , err := os .OpenFile (file , os .O_WRONLY | os .O_CREATE | os .O_TRUNC , f .Mode ())
152
+ if err != nil {
153
+ return err
154
+ }
155
+
156
+ r , err := f .Open ()
157
+ if err != nil {
158
+ return err
159
+ }
160
+
161
+ if _ , err := io .Copy (output , r ); err != nil {
162
+ return err
163
+ }
164
+
165
+ output .Close ()
166
+ r .Close ()
167
+ }
168
+
169
+ return nil
170
+ }
171
+
90
172
const defaultVersionContraint = "> 0"
91
173
92
174
func (m * PluginManager ) getProviderRemote (provider , v string ) (discovery.PluginMeta , bool , error ) {
93
175
if v == "" {
94
176
v = defaultVersionContraint
95
177
}
96
178
179
+ m .getProviderRemoteDirectDownload (provider , v )
97
180
installer := & discovery.ProviderInstaller {
98
181
Dir : m .Path ,
99
182
PluginProtocolVersion : discovery .PluginInstallProtocolVersion ,
0 commit comments