Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions ironic/resource_ironic_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func resourceDeployment() *schema.Resource {
Optional: true,
ForceNew: true,
},
"user_data_url_headers": {
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
},
"network_data": {
Type: schema.TypeMap,
Optional: true,
Expand Down Expand Up @@ -105,9 +110,13 @@ func resourceDeploymentCreate(d *schema.ResourceData, meta interface{}) error {
userData := d.Get("user_data").(string)
userDataURL := d.Get("user_data_url").(string)
userDataCaCert := d.Get("user_data_url_ca_cert").(string)
userDataHeaders := d.Get("user_data_url_headers").(map[string]interface{})

// if user_data_url is specified in addition to user_data, use the former
ignitionData := fetchFullIgnition(userDataURL, userDataCaCert)
ignitionData, err := fetchFullIgnition(userDataURL, userDataCaCert, userDataHeaders)
if err != nil {
return fmt.Errorf("could not fetch data from user_data_url: %s", err)
}
if ignitionData != "" {
userData = ignitionData
}
Expand All @@ -125,7 +134,7 @@ func resourceDeploymentCreate(d *schema.ResourceData, meta interface{}) error {
}

// fetchFullIgnition gets full igntion from the URL and cert passed to it and returns userdata as a string
func fetchFullIgnition(userDataURL string, userDataCaCert string) string {
func fetchFullIgnition(userDataURL string, userDataCaCert string, userDataHeaders map[string]interface{}) (string, error) {
Comment thread
hardys marked this conversation as resolved.
// Send full ignition, if the URL is specified
if userDataURL != "" {
caCertPool := x509.NewCertPool()
Expand All @@ -135,7 +144,7 @@ func fetchFullIgnition(userDataURL string, userDataCaCert string) string {
caCert, err := base64.StdEncoding.DecodeString(userDataCaCert)
if err != nil {
log.Printf("could not decode user_data_url_ca_cert: %s", err)
return ""
return "", err
}
caCertPool.AppendCertsFromPEM(caCert)

Expand All @@ -149,21 +158,31 @@ func fetchFullIgnition(userDataURL string, userDataCaCert string) string {
client.HTTPClient.Transport = transport

// Get the data
resp, err := client.Get(userDataURL)
req, err := retryablehttp.NewRequest("GET", userDataURL, nil)
if err != nil {
log.Printf("could not get user_data_url: %s", err)
return "", err
}
if userDataHeaders != nil {
for k, v := range userDataHeaders {
req.Header.Add(k, v.(string))
}
}
resp, err := client.Do(req)
if err != nil {
log.Printf("could not get user_data_url: %s", err)
return ""
return "", err
}
defer resp.Body.Close()
var userData []byte
userData, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("could not read user_data_url: %s", err)
return ""
return "", err
}
return string(userData)
return string(userData), nil
}
return ""
return "", nil
}

// buildConfigDrive handles building a config drive appropriate for the Ironic version we are using. Newer versions
Expand Down
68 changes: 43 additions & 25 deletions ironic/resource_ironic_deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ func testAccDeploymentResource(node, resourceClass, allocation string) string {
func TestFetchFullIgnition(t *testing.T) {
// Setup a fake https endpoint to server full ignition
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for k, v := range r.Header {
if k == "Test" {
fmt.Fprintf(w, "Header: %s=%s\n", k, v)
}
}
fmt.Fprintln(w, "Full Ignition")
}))
defer server.Close()
Expand All @@ -131,45 +136,58 @@ func TestFetchFullIgnition(t *testing.T) {
},
)
certB64 := base64.URLEncoding.EncodeToString(certInPem)
emptyHeaders := make(map[string]interface{})

testCases := []struct {
Scenario string
UserDataURL string
UserDataURLCACert string
ExpectResult bool
Scenario string
UserDataURL string
UserDataURLCACert string
UserDataURLHeaders map[string]interface{}
ExpectedResult string
}{
{
Scenario: "user data url and ca cert present",
UserDataURL: server.URL,
UserDataURLCACert: certB64,
ExpectResult: true,
Scenario: "user data url and ca cert present",
UserDataURL: server.URL,
UserDataURLCACert: certB64,
UserDataURLHeaders: emptyHeaders,
ExpectedResult: "Full Ignition\n",
},
{
Scenario: "user data url present but no ca cert",
UserDataURL: server.URL,
UserDataURLCACert: "",
UserDataURLHeaders: emptyHeaders,
ExpectedResult: "Full Ignition\n",
},
{
Scenario: "user data url present but no ca cert",
UserDataURL: server.URL,
UserDataURLCACert: "",
ExpectResult: true,
Scenario: "user data url, ca cert and headers present",
UserDataURL: server.URL,
UserDataURLCACert: certB64,
UserDataURLHeaders: map[string]interface{}{"Test": "foo"},
ExpectedResult: "Header: Test=[foo]\nFull Ignition\n",
},
{
Scenario: "user data url is not present but ca cert is",
UserDataURL: "",
UserDataURLCACert: certB64,
ExpectResult: false,
Scenario: "user data url is not present but ca cert is",
UserDataURL: "",
UserDataURLCACert: certB64,
UserDataURLHeaders: emptyHeaders,
ExpectedResult: "",
},
{
Scenario: "neither user data url nor ca cert is not present",
UserDataURL: "",
UserDataURLCACert: "",
ExpectResult: false,
Scenario: "neither user data url nor ca cert is not present",
UserDataURL: "",
UserDataURLCACert: "",
UserDataURLHeaders: emptyHeaders,
ExpectedResult: "",
},
}
for _, tc := range testCases {
userData := fetchFullIgnition(tc.UserDataURL, tc.UserDataURLCACert)
if tc.ExpectResult && (userData != "Full Ignition\n") {
t.Errorf("expected userData: %s, got %s", "Full Ignition\n", userData)
userData, err := fetchFullIgnition(tc.UserDataURL, tc.UserDataURLCACert, tc.UserDataURLHeaders)
if err != nil {
t.Errorf("expected err: %s", err)
}
if !tc.ExpectResult && (userData != "") {
t.Errorf("expected userData: %s, got %s", "", userData)
if userData != tc.ExpectedResult {
t.Errorf("expected userData: %s, got %s", tc.ExpectedResult, userData)
}
}
}