github.com/aznashwan/terraform@v0.4.3-0.20151118032030-21f93ca4558d/state/remote/http.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "crypto/tls" 7 "encoding/base64" 8 "fmt" 9 "io" 10 "net/http" 11 "net/url" 12 "strconv" 13 ) 14 15 func httpFactory(conf map[string]string) (Client, error) { 16 address, ok := conf["address"] 17 if !ok { 18 return nil, fmt.Errorf("missing 'address' configuration") 19 } 20 21 url, err := url.Parse(address) 22 if err != nil { 23 return nil, fmt.Errorf("failed to parse HTTP URL: %s", err) 24 } 25 if url.Scheme != "http" && url.Scheme != "https" { 26 return nil, fmt.Errorf("address must be HTTP or HTTPS") 27 } 28 29 client := &http.Client{} 30 if skipRaw, ok := conf["skip_cert_verification"]; ok { 31 skip, err := strconv.ParseBool(skipRaw) 32 if err != nil { 33 return nil, fmt.Errorf("skip_cert_verification must be boolean") 34 } 35 if skip { 36 // Replace the client with one that ignores TLS verification 37 client = &http.Client{ 38 Transport: &http.Transport{ 39 TLSClientConfig: &tls.Config{ 40 InsecureSkipVerify: true, 41 }, 42 }, 43 } 44 } 45 } 46 47 return &HTTPClient{ 48 URL: url, 49 Client: client, 50 }, nil 51 } 52 53 // HTTPClient is a remote client that stores data in Consul or HTTP REST. 54 type HTTPClient struct { 55 URL *url.URL 56 Client *http.Client 57 } 58 59 func (c *HTTPClient) Get() (*Payload, error) { 60 resp, err := c.Client.Get(c.URL.String()) 61 if err != nil { 62 return nil, err 63 } 64 defer resp.Body.Close() 65 66 // Handle the common status codes 67 switch resp.StatusCode { 68 case http.StatusOK: 69 // Handled after 70 case http.StatusNoContent: 71 return nil, nil 72 case http.StatusNotFound: 73 return nil, nil 74 case http.StatusUnauthorized: 75 return nil, fmt.Errorf("HTTP remote state endpoint requires auth") 76 case http.StatusForbidden: 77 return nil, fmt.Errorf("HTTP remote state endpoint invalid auth") 78 case http.StatusInternalServerError: 79 return nil, fmt.Errorf("HTTP remote state internal server error") 80 default: 81 return nil, fmt.Errorf("Unexpected HTTP response code %d", resp.StatusCode) 82 } 83 84 // Read in the body 85 buf := bytes.NewBuffer(nil) 86 if _, err := io.Copy(buf, resp.Body); err != nil { 87 return nil, fmt.Errorf("Failed to read remote state: %s", err) 88 } 89 90 // Create the payload 91 payload := &Payload{ 92 Data: buf.Bytes(), 93 } 94 95 // If there was no data, then return nil 96 if len(payload.Data) == 0 { 97 return nil, nil 98 } 99 100 // Check for the MD5 101 if raw := resp.Header.Get("Content-MD5"); raw != "" { 102 md5, err := base64.StdEncoding.DecodeString(raw) 103 if err != nil { 104 return nil, fmt.Errorf( 105 "Failed to decode Content-MD5 '%s': %s", raw, err) 106 } 107 108 payload.MD5 = md5 109 } else { 110 // Generate the MD5 111 hash := md5.Sum(payload.Data) 112 payload.MD5 = hash[:] 113 } 114 115 return payload, nil 116 } 117 118 func (c *HTTPClient) Put(data []byte) error { 119 // Copy the target URL 120 base := *c.URL 121 122 // Generate the MD5 123 hash := md5.Sum(data) 124 b64 := base64.StdEncoding.EncodeToString(hash[:]) 125 126 /* 127 // Set the force query parameter if needed 128 if force { 129 values := base.Query() 130 values.Set("force", "true") 131 base.RawQuery = values.Encode() 132 } 133 */ 134 135 req, err := http.NewRequest("POST", base.String(), bytes.NewReader(data)) 136 if err != nil { 137 return fmt.Errorf("Failed to make HTTP request: %s", err) 138 } 139 140 // Prepare the request 141 req.Header.Set("Content-Type", "application/octet-stream") 142 req.Header.Set("Content-MD5", b64) 143 req.ContentLength = int64(len(data)) 144 145 // Make the request 146 resp, err := c.Client.Do(req) 147 if err != nil { 148 return fmt.Errorf("Failed to upload state: %v", err) 149 } 150 defer resp.Body.Close() 151 152 // Handle the error codes 153 switch resp.StatusCode { 154 case http.StatusOK: 155 return nil 156 default: 157 return fmt.Errorf("HTTP error: %d", resp.StatusCode) 158 } 159 } 160 161 func (c *HTTPClient) Delete() error { 162 req, err := http.NewRequest("DELETE", c.URL.String(), nil) 163 if err != nil { 164 return fmt.Errorf("Failed to make HTTP request: %s", err) 165 } 166 167 // Make the request 168 resp, err := c.Client.Do(req) 169 if err != nil { 170 return fmt.Errorf("Failed to delete state: %s", err) 171 } 172 defer resp.Body.Close() 173 174 // Handle the error codes 175 switch resp.StatusCode { 176 case http.StatusOK: 177 return nil 178 default: 179 return fmt.Errorf("HTTP error: %d", resp.StatusCode) 180 } 181 }