11package terraform
22
33import (
4+ "archive/zip"
45 "fmt"
6+ "io"
7+ "io/ioutil"
8+ "net/http"
59 "os"
610 "os/exec"
11+ "path/filepath"
12+ "runtime"
713 "strings"
814
915 "github.com/hashicorp/go-hclog"
@@ -28,6 +34,11 @@ type PluginManager struct {
2834func (m * PluginManager ) Provider (provider , version string , forceLocal bool ) (* plugin.Client , discovery.PluginMeta , error ) {
2935 meta , ok := m .getLocal ("provider" , provider , version )
3036 if ! ok && ! forceLocal {
37+ meta , ok , _ = m .getProviderRemoteDirectDownload (provider , version )
38+ if ok {
39+ return client (meta ), meta , nil
40+ }
41+
3142 var err error
3243 meta , _ , err = m .getProviderRemote (provider , version )
3344 if err != nil {
@@ -87,13 +98,85 @@ func client(m discovery.PluginMeta) *plugin.Client {
8798 })
8899}
89100
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+
90172const defaultVersionContraint = "> 0"
91173
92174func (m * PluginManager ) getProviderRemote (provider , v string ) (discovery.PluginMeta , bool , error ) {
93175 if v == "" {
94176 v = defaultVersionContraint
95177 }
96178
179+ m .getProviderRemoteDirectDownload (provider , v )
97180 installer := & discovery.ProviderInstaller {
98181 Dir : m .Path ,
99182 PluginProtocolVersion : discovery .PluginInstallProtocolVersion ,
0 commit comments