diff --git a/internal/provider/cloudinit_config.go b/internal/provider/cloudinit_config.go index 99bdd31..b51ae38 100644 --- a/internal/provider/cloudinit_config.go +++ b/internal/provider/cloudinit_config.go @@ -147,6 +147,15 @@ func (c *configModel) update(ctx context.Context) diag.Diagnostics { return diags } +func is7bit(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > 0x7F { + return false + } + } + return true +} + func renderPartsToWriter(ctx context.Context, mimeBoundary string, parts []configPartModel, writer io.Writer) error { mimeWriter := multipart.NewWriter(writer) defer func() { @@ -173,11 +182,19 @@ func renderPartsToWriter(ctx context.Context, mimeBoundary string, parts []confi } for _, part := range parts { + encode_to_base64 := !is7bit(part.Content.ValueString()) + var cte string + if encode_to_base64 { + cte = "base64" + } else { + cte = "7bit" + } + header := textproto.MIMEHeader{} header.Set("Content-Type", part.ContentType.ValueString()) header.Set("MIME-Version", "1.0") - header.Set("Content-Transfer-Encoding", "7bit") + header.Set("Content-Transfer-Encoding", cte) if part.FileName.ValueString() != "" { header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, part.FileName.ValueString())) @@ -187,12 +204,20 @@ func renderPartsToWriter(ctx context.Context, mimeBoundary string, parts []confi header.Set("X-Merge-Type", part.MergeType.ValueString()) } + var err error + partWriter, err := mimeWriter.CreatePart(header) if err != nil { return err } - _, err = partWriter.Write([]byte(part.Content.ValueString())) + if encode_to_base64 { + base64Writer := base64.NewEncoder(base64.StdEncoding, partWriter) + defer base64Writer.Close() + _, err = base64Writer.Write([]byte(part.Content.ValueString())) + } else { + _, err = partWriter.Write([]byte(part.Content.ValueString())) + } if err != nil { return err } diff --git a/internal/provider/data_source_cloudinit_config_test.go b/internal/provider/data_source_cloudinit_config_test.go index 5d217d8..828cdde 100644 --- a/internal/provider/data_source_cloudinit_config_test.go +++ b/internal/provider/data_source_cloudinit_config_test.go @@ -29,6 +29,19 @@ func TestConfigDataSourceRender(t *testing.T) { }`, "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", }, + { + "no gzip or b64 - non-ASCII content", + `data "cloudinit_config" "foo" { + gzip = false + base64_encode = false + + part { + content_type = "text/cloud-config" + content = "¡Hola mundo!" + } + }`, + "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: base64\r\nContent-Type: text/cloud-config\r\nMime-Version: 1.0\r\n\r\nwqFIb2xhIG11bmRvIQ==\r\n--MIMEBOUNDARY--\r\n", + }, { "no gzip or b64 - basic content - default to text plain", `data "cloudinit_config" "foo" { diff --git a/internal/provider/resource_cloudinit_config_test.go b/internal/provider/resource_cloudinit_config_test.go index 654e89c..77c2cc9 100644 --- a/internal/provider/resource_cloudinit_config_test.go +++ b/internal/provider/resource_cloudinit_config_test.go @@ -29,6 +29,19 @@ func TestConfigResourceRender(t *testing.T) { }`, "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", }, + { + "no gzip or b64 - non-ASCII content", + `resource "cloudinit_config" "foo" { + gzip = false + base64_encode = false + + part { + content_type = "text/cloud-config" + content = "¡Hola mundo!" + } + }`, + "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: base64\r\nContent-Type: text/cloud-config\r\nMime-Version: 1.0\r\n\r\nwqFIb2xhIG11bmRvIQ==\r\n--MIMEBOUNDARY--\r\n", + }, { "no gzip or b64 - basic content - default to text plain", `resource "cloudinit_config" "foo" {